Refine performance-optimization skill and checklist based on feedback

- Optimize Hero/LCP images: use aspect-ratio, fetchpriority="high", and decoding="sync".
- Optimize below-the-fold images: add decoding="async" alongside loading="lazy".
- Update tree-shaking advice: clarify ESM and sideEffects: false requirements.
- Add Suspense wrapper to code-splitting examples for completeness.
- Ensure image checks include both dimensions and aspect-ratio as non-exclusive.
This commit is contained in:
Joan Leon committed 2026-04-08 08:37:26 +02:00
1 parent c2c4f56d05
commit 7a9887cedb
2 files changed
+20 -10

No files matched your search

+4 -4
View File
@@ -31,14 +31,14 @@ When TTFB is slow (> 600ms), check each component in DevTools Network waterfall:
### Images
- [ ] Images use modern formats (WebP, AVIF)
- [ ] Images are responsively sized (`srcset` and `sizes`)
- [ ] Images have explicit `width` and `height` attributes (prevents CLS)
- [ ] Below-the-fold images use `loading="lazy"`
- [ ] Hero/LCP images use `fetchpriority="high"` and no lazy loading
- [ ] Images have explicit `width` and `height` attributes and/or CSS `aspect-ratio` (prevents CLS)
- [ ] Below-the-fold images use `loading="lazy"` and `decoding="async"`
- [ ] Hero/LCP images use `fetchpriority="high"`, `decoding="sync"`, and no lazy loading
### JavaScript
- [ ] Bundle size under 200KB gzipped (initial load)
- [ ] Code splitting with dynamic `import()` for routes and heavy features
- [ ] Tree shaking enabled (no dead code in production bundle)
- [ ] Tree shaking enabled (verify dependency ships ESM and marks `sideEffects: false`)
- [ ] No blocking JavaScript in `<head>` (use `defer` or `async`)
- [ ] Heavy computation offloaded to Web Workers (if applicable)
- [ ] `React.memo()` on expensive components that re-render with same props
+16 -6
View File
@@ -187,20 +187,21 @@ const tasks = await db.tasks.findMany({
type="image/webp"
/>
<img
src="/hero-mobile.jpg"
width="1200"
height="600"
src="/hero-desktop.jpg"
style="aspect-ratio: 1200 / 600"
fetchpriority="high"
decoding="sync"
alt="Hero image description"
/>
</picture>
<!-- GOOD: Below-the-fold image — lazy loaded -->
<!-- GOOD: Below-the-fold image — lazy loaded + async decoding -->
<img
src="/content.webp"
width="800"
height="400"
loading="lazy"
decoding="async"
alt="Content image description"
/>
```
@@ -234,14 +235,23 @@ function TaskStats({ tasks }: Props) {
#### Large Bundle Size
```typescript
// Modern bundlers (Vite, webpack 5+) handle named imports with tree-shaking automatically.
// Modern bundlers (Vite, webpack 5+) handle named imports with tree-shaking automatically,
// provided the dependency ships ESM and is marked `sideEffects: false` in package.json.
// Profile before changing import styles — the real gains come from splitting and lazy loading.
// GOOD: Dynamic import for heavy, rarely-used features
const ChartLibrary = lazy(() => import('./ChartLibrary'));
// GOOD: Route-level code splitting
// GOOD: Route-level code splitting wrapped in Suspense
const SettingsPage = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<Spinner />}>
<SettingsPage />
</Suspense>
);
}
```
#### Missing Caching (Backend)