643 lines
12 KiB
Markdown
643 lines
12 KiB
Markdown
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:
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# 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):
|
|
```javascript
|
|
// Routes are auto-split
|
|
// src/routes/+page.svelte → separate chunk
|
|
```
|
|
|
|
**React**:
|
|
```javascript
|
|
import { lazy, Suspense } from 'react';
|
|
|
|
const ExpenseList = lazy(() => import('./ExpenseList'));
|
|
|
|
function App() {
|
|
return (
|
|
<Suspense fallback={<Loading />}>
|
|
<ExpenseList />
|
|
</Suspense>
|
|
);
|
|
}
|
|
```
|
|
|
|
**Vue**:
|
|
```javascript
|
|
const ExpenseList = () => import('./ExpenseList.vue');
|
|
|
|
const routes = [
|
|
{
|
|
path: '/expenses',
|
|
component: ExpenseList // Lazy loaded
|
|
}
|
|
];
|
|
```
|
|
|
|
#### Manual Code Splitting
|
|
```javascript
|
|
// Split heavy utility into separate chunk
|
|
const heavyUtil = await import('./heavyUtility.js');
|
|
heavyUtil.doSomething();
|
|
```
|
|
|
|
### 2. Tree Shaking 🌳
|
|
|
|
#### Remove Unused Code
|
|
```javascript
|
|
// Bad: Imports everything
|
|
import * as utils from './utils';
|
|
|
|
// Good: Import only what you need
|
|
import { formatDate, formatCurrency } from './utils';
|
|
```
|
|
|
|
#### Check for Unused Dependencies
|
|
```bash
|
|
npm install -g depcheck
|
|
depcheck
|
|
|
|
# Remove unused packages
|
|
npm uninstall <unused-package>
|
|
```
|
|
|
|
### 3. Optimize Dependencies 📚
|
|
|
|
#### Use Lighter Alternatives
|
|
```javascript
|
|
// 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
|
|
```bash
|
|
# 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
|
|
```javascript
|
|
// 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)
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// Use environment variables
|
|
if (import.meta.env.DEV) {
|
|
console.log('Debug info'); // Removed in production
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Caching Optimization 💾
|
|
|
|
### 1. Optimize Cache Strategy
|
|
|
|
#### Review Cache Settings
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```json
|
|
{
|
|
"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
|
|
```bash
|
|
# Use optimized PNG or WebP
|
|
# Compress images
|
|
# Provide multiple sizes
|
|
```
|
|
|
|
---
|
|
|
|
## Network Optimization 🌐
|
|
|
|
### 1. Reduce API Calls
|
|
|
|
#### Batch Requests
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// Load initial data
|
|
const initial = await fetchExpenses({ limit: 20 });
|
|
|
|
// Load more on scroll
|
|
function onScroll() {
|
|
if (nearBottom) {
|
|
loadMore();
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Optimize Requests
|
|
```javascript
|
|
// 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:
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
import { debounce } from './utils';
|
|
|
|
const handleSearch = debounce((query) => {
|
|
searchExpenses(query);
|
|
}, 300); // Wait 300ms after typing stops
|
|
```
|
|
|
|
### 3. Optimize Rendering
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
1. Open Chrome DevTools
|
|
2. Go to Lighthouse tab
|
|
3. Select categories:
|
|
- ✅ Performance
|
|
- ✅ Progressive Web App
|
|
- ✅ Best Practices
|
|
- ✅ Accessibility
|
|
- ✅ SEO
|
|
4. 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
|
|
```javascript
|
|
// 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
|
|
```bash
|
|
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:
|
|
1. **Enable compression** (already in Vite)
|
|
2. **Remove console.logs** in production
|
|
3. **Lazy load routes** (easy with frameworks)
|
|
4. **Optimize images** (use WebP)
|
|
5. **Limit initial data fetch** (pagination)
|
|
|
|
### Medium Effort:
|
|
1. **Analyze and split bundles**
|
|
2. **Replace heavy dependencies**
|
|
3. **Implement virtual scrolling**
|
|
4. **Optimize service worker**
|
|
5. **Add performance monitoring**
|
|
|
|
### Long Term:
|
|
1. **Regular Lighthouse audits**
|
|
2. **Monitor real user metrics**
|
|
3. **Continuous optimization**
|
|
4. **Keep dependencies updated**
|
|
5. **Review and refactor regularly**
|