Initial commit
This commit is contained in:
642
commands/optimize.md
Normal file
642
commands/optimize.md
Normal file
@@ -0,0 +1,642 @@
|
||||
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**
|
||||
Reference in New Issue
Block a user