Files
gh-jamshu-jamshi-marketplac…/commands/optimize.md
2025-11-29 18:50:06 +08:00

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';
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

  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

// 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"
  • /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