12 KiB
12 KiB
Analyze and optimize your Odoo PWA for performance, bundle size, and user experience.
What this command does:
- Analyzes bundle size
- Identifies performance bottlenecks
- Suggests optimizations
- Checks caching efficiency
- Reviews PWA configuration
- Provides actionable recommendations
Quick Performance Check 🚀
Run These Commands:
# Build for production
npm run build
# Analyze bundle size
npm run build -- --analyze
# Preview production build
npm run preview
# Run Lighthouse audit
# (Chrome DevTools → Lighthouse tab)
Bundle Size Analysis 📦
Check Current Size
# Build and see output
npm run build
# Typical output:
# dist/index.html 1.2 kB
# dist/assets/index-abc123.js 45.3 kB
# dist/assets/vendor-def456.js 120.5 kB
Analyze Bundle Composition
# Install analyzer
npm install -D rollup-plugin-visualizer
# Add to vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';
export default {
plugins: [
// ...other plugins
visualizer({
open: true,
gzipSize: true,
brotliSize: true
})
]
};
# Build and open report
npm run build
# Opens stats.html in browser
Target Bundle Sizes:
- ✅ Excellent: < 200 KB (gzipped)
- ⚠️ Good: 200-500 KB
- ❌ Needs Work: > 500 KB
Optimization Strategies
1. Code Splitting 📂
Lazy Load Routes
SvelteKit (automatic):
// Routes are auto-split
// src/routes/+page.svelte → separate chunk
React:
import { lazy, Suspense } from 'react';
const ExpenseList = lazy(() => import('./ExpenseList'));
function App() {
return (
<Suspense fallback={<Loading />}>
<ExpenseList />
</Suspense>
);
}
Vue:
const ExpenseList = () => import('./ExpenseList.vue');
const routes = [
{
path: '/expenses',
component: ExpenseList // Lazy loaded
}
];
Manual Code Splitting
// Split heavy utility into separate chunk
const heavyUtil = await import('./heavyUtility.js');
heavyUtil.doSomething();
2. Tree Shaking 🌳
Remove Unused Code
// Bad: Imports everything
import * as utils from './utils';
// Good: Import only what you need
import { formatDate, formatCurrency } from './utils';
Check for Unused Dependencies
npm install -g depcheck
depcheck
# Remove unused packages
npm uninstall <unused-package>
3. Optimize Dependencies 📚
Use Lighter Alternatives
// Instead of moment.js (heavy)
import moment from 'moment'; // 72 KB
// Use date-fns (tree-shakeable)
import { format } from 'date-fns'; // ~2 KB
// Or native Date
new Date().toLocaleDateString();
Common Heavy Packages & Alternatives:
- ❌ moment (72 KB) → ✅ date-fns (tree-shakeable)
- ❌ lodash (whole) → ✅ lodash-es (individual imports)
- ❌ axios → ✅ fetch (built-in)
- ❌ uuid → ✅ crypto.randomUUID() (built-in)
4. Optimize Images 🖼️
Compress Images
# Install image optimizer
npm install -D vite-plugin-imagemin
# Add to vite.config.js
import viteImagemin from 'vite-plugin-imagemin';
export default {
plugins: [
viteImagemin({
gifsicle: { optimizationLevel: 7 },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
svgo: { plugins: [{ removeViewBox: false }] }
})
]
};
Lazy Load Images
// Native lazy loading
<img src="large-image.jpg" loading="lazy" />
// Or with IntersectionObserver
Use Appropriate Formats
- WebP for photos (smaller than JPEG)
- SVG for icons/logos
- PNG only when transparency needed
5. Minimize JavaScript ⚡
Enable Minification (default in Vite)
// vite.config.js
export default {
build: {
minify: 'terser', // or 'esbuild' (faster)
terserOptions: {
compress: {
drop_console: true, // Remove console.logs
drop_debugger: true
}
}
}
};
Remove Development Code
// Use environment variables
if (import.meta.env.DEV) {
console.log('Debug info'); // Removed in production
}
Caching Optimization 💾
1. Optimize Cache Strategy
Review Cache Settings
// In cache store
const CACHE_VALIDITY = 5 * 60 * 1000; // 5 minutes
// Consider your use case:
// - Frequently changing data: 1-2 minutes
// - Moderate updates: 5-10 minutes
// - Rarely changes: 30-60 minutes
Limit Initial Load
// Don't fetch everything at once
const records = await odoo.searchRecords(
model,
[],
fields,
100 // Limit to 100 records initially
);
// Load more on demand (pagination)
2. Optimize Sync Frequency
// Adjust sync interval based on needs
const SYNC_INTERVAL = 5 * 60 * 1000; // 5 minutes
// Balance:
// - More frequent: Better UX, more bandwidth
// - Less frequent: Less bandwidth, slightly stale data
3. Selective Caching
// Only cache fields you need
const fields = [
'x_studio_name',
'x_studio_amount',
'x_studio_date'
// Don't fetch unnecessary fields
];
PWA Optimization 📱
1. Service Worker Configuration
Optimize Caching Strategy
// In service worker or vite-plugin-pwa config
VitePWA({
workbox: {
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/,
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts',
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365 // 1 year
}
}
},
{
urlPattern: /^https:\/\/.*\.odoo\.com\/.*/,
handler: 'NetworkFirst',
options: {
cacheName: 'odoo-api',
networkTimeoutSeconds: 3
}
}
]
}
})
2. Optimize Manifest
{
"name": "Expense Tracker",
"short_name": "Expenses",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0066cc",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
3. Optimize Icons
# Use optimized PNG or WebP
# Compress images
# Provide multiple sizes
Network Optimization 🌐
1. Reduce API Calls
Batch Requests
// Bad: Multiple calls
const expenses = await fetchExpenses();
const tasks = await fetchTasks();
const partners = await fetchPartners();
// Good: Single call (if Odoo supports)
const data = await fetchAll(['expenses', 'tasks', 'partners']);
Cache Partner Names
// Fetch once, reuse
const partners = await odoo.fetchPartners();
localStorage.setItem('partnerCache', JSON.stringify(partners));
// Later, use cached data
const cached = JSON.parse(localStorage.getItem('partnerCache'));
2. Incremental Loading
// Load initial data
const initial = await fetchExpenses({ limit: 20 });
// Load more on scroll
function onScroll() {
if (nearBottom) {
loadMore();
}
}
3. Optimize Requests
// Only fetch fields you display
const fields = ['id', 'x_studio_name', 'x_studio_amount'];
// Use appropriate domain filters
const domain = [
['x_studio_date', '>=', lastMonth],
['x_studio_status', '!=', 'deleted']
];
UI Performance 🎨
1. Virtual Scrolling
For long lists:
// SvelteKit
import { VirtualList } from 'svelte-virtual-list';
<VirtualList items={expenses} let:item>
<ExpenseCard expense={item} />
</VirtualList>
// React
import { FixedSizeList } from 'react-window';
// Vue
import { RecycleScroller } from 'vue-virtual-scroller';
2. Debounce Search
import { debounce } from './utils';
const handleSearch = debounce((query) => {
searchExpenses(query);
}, 300); // Wait 300ms after typing stops
3. Optimize Rendering
// SvelteKit: Use keyed each blocks
{#each expenses as expense (expense.id)}
<ExpenseCard {expense} />
{/each}
// React: Use keys and memo
const ExpenseCard = memo(({ expense }) => {
// ...
});
expenses.map(expense => (
<ExpenseCard key={expense.id} expense={expense} />
));
// Vue: Use v-memo
<ExpenseCard
v-for="expense in expenses"
:key="expense.id"
:expense="expense"
v-memo="[expense.id]"
/>
Build Optimization 🔨
Vite Config Optimizations
// vite.config.js
export default {
build: {
// Target modern browsers
target: 'es2020',
// Optimize chunks
rollupOptions: {
output: {
manualChunks: {
vendor: ['svelte', '@sveltejs/kit'],
odoo: ['./src/lib/odoo.js', './src/lib/stores']
}
}
},
// Compression
minify: 'esbuild', // Faster than terser
// Source maps (only for debugging)
sourcemap: false
},
// Optimize dependencies
optimizeDeps: {
include: ['date-fns', 'lodash-es']
}
};
Lighthouse Audit 💡
Run Lighthouse
- Open Chrome DevTools
- Go to Lighthouse tab
- Select categories:
- ✅ Performance
- ✅ Progressive Web App
- ✅ Best Practices
- ✅ Accessibility
- ✅ SEO
- Click "Analyze page load"
Target Scores:
- Performance: > 90
- PWA: 100
- Best Practices: > 95
- Accessibility: > 90
- SEO: > 90
Common Issues & Fixes:
Low Performance Score
- Reduce bundle size
- Optimize images
- Enable caching
- Lazy load resources
PWA Issues
- Fix manifest.json
- Register service worker
- Add offline support
- Provide app icons
Accessibility Issues
- Add alt text to images
- Use semantic HTML
- Ensure color contrast
- Add ARIA labels
Performance Monitoring 📊
Add Performance Tracking
// Measure initial load
window.addEventListener('load', () => {
const perfData = performance.getEntriesByType('navigation')[0];
console.log('Load time:', perfData.loadEventEnd - perfData.fetchStart);
console.log('DOM ready:', perfData.domContentLoadedEventEnd - perfData.fetchStart);
});
// Measure sync time
console.time('sync');
await expenseCache.refresh();
console.timeEnd('sync');
Use Web Vitals
npm install web-vitals
# In app
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);
Optimization Checklist ✅
Bundle Size:
□ Analyzed bundle composition
□ Removed unused dependencies
□ Lazy loaded routes/components
□ Optimized images
□ Tree-shaking enabled
□ Minification enabled
□ Total size < 500 KB (gzipped)
Caching:
□ Optimal cache validity period
□ Incremental sync working
□ Selective field fetching
□ Partner name caching
□ Appropriate sync frequency
□ IndexedDB optimized
PWA:
□ Service worker registered
□ Offline mode works
□ App installable
□ Icons optimized
□ Manifest configured
□ Fast load time
Performance:
□ Lighthouse score > 90
□ Initial load < 3s
□ Sync time < 2s
□ No console errors
□ Smooth scrolling
□ Fast navigation
Example prompts to use this command:
/optimize- Run optimization checks- User: "Make my app faster"
- User: "Reduce bundle size"
- User: "Optimize performance"
Related Commands:
/test-connection- Test after optimization/update-deps- Update to faster versions/troubleshoot- Fix performance issues/help- More information
Quick Wins 🎯
Immediate Optimizations:
- Enable compression (already in Vite)
- Remove console.logs in production
- Lazy load routes (easy with frameworks)
- Optimize images (use WebP)
- Limit initial data fetch (pagination)
Medium Effort:
- Analyze and split bundles
- Replace heavy dependencies
- Implement virtual scrolling
- Optimize service worker
- Add performance monitoring
Long Term:
- Regular Lighthouse audits
- Monitor real user metrics
- Continuous optimization
- Keep dependencies updated
- Review and refactor regularly