SkillHub

afrexai-web-performance-engine

v1.0.0

全面审计网页性能,诊断瓶颈,针对服务器、渲染、首屏元素、JavaScript及布局提供修复方案。

Sourced from ClawHub, Authored by 1kalin

Installation

Please help me install the skill `afrexai-web-performance-engine` from SkillHub official store. npx skills add 1kalin/afrexai-web-performance-engine

Web Performance Engine

Complete web performance optimization system. Audit, diagnose, fix, and monitor — no external tools required (but integrates with Lighthouse, WebPageTest, Chrome DevTools when available).

Phase 1: Performance Audit

Quick Health Check

Run these checks in order. Stop when you find the bottleneck tier.

Tier 1 — Critical (blocks rendering): - [ ] Time to First Byte (TTFB) > 800ms → server problem - [ ] First Contentful Paint (FCP) > 1.8s → render-blocking resources - [ ] Largest Contentful Paint (LCP) > 2.5s → hero element problem - [ ] Total Blocking Time (TBT) > 200ms → JavaScript problem - [ ] Cumulative Layout Shift (CLS) > 0.1 → layout instability - [ ] Interaction to Next Paint (INP) > 200ms → event handler problem

Tier 2 — Important (affects experience): - [ ] Page weight > 2MB - [ ] Requests > 80 - [ ] JavaScript > 500KB (compressed) - [ ] Images > 1MB total - [ ] No compression (gzip/brotli) - [ ] No caching headers

Tier 3 — Polish (competitive edge): - [ ] Speed Index > 3.4s - [ ] Time to Interactive > 3.8s - [ ] Font loading causes flash - [ ] Third-party scripts > 30% of JS

Audit Brief Template

audit:
  url: ""
  device: "mobile"  # mobile | desktop | both
  connection: "4G"  # 3G | 4G | fiber
  region: ""        # closest to target users

scores:
  performance: null  # 0-100
  fcp_ms: null
  lcp_ms: null
  tbt_ms: null
  cls: null
  inp_ms: null
  ttfb_ms: null

page_weight:
  total_kb: null
  html_kb: null
  css_kb: null
  js_kb: null
  images_kb: null
  fonts_kb: null
  other_kb: null

requests:
  total: null
  by_type: {}
  third_party_count: null
  third_party_kb: null

Getting Metrics Without Tools

If no Lighthouse/DevTools available, use web-based tools: 1. web_fetch "https://pagespeed.web.dev/analysis?url={encoded_url}" — Google's free tool 2. web_search "webpagetest {url}" — find cached results 3. web_search "site:{domain} core web vitals" — find CrUX data 4. Check <head> for obvious issues: render-blocking CSS/JS, missing preloads, no meta viewport

Phase 2: Diagnosis — The Performance Waterfall

Critical Rendering Path Analysis

DNS → TCP → TLS → TTFB → HTML Parse → CSSOM → Render Tree → FCP → LCP
                                ↓
                         JS Download → Parse → Execute → INP

Bottleneck Decision Tree:

High TTFB (>800ms)?
├─ YES → Phase 3A: Server optimization
└─ NO → High FCP (>1.8s)?
    ├─ YES → Phase 3B: Render-blocking resources
    └─ NO → High LCP (>2.5s)?
        ├─ YES → Phase 3C: Hero element optimization
        └─ NO → High TBT (>200ms)?
            ├─ YES → Phase 3D: JavaScript optimization
            └─ NO → High CLS (>0.1)?
                ├─ YES → Phase 3E: Layout stability
                └─ NO → High INP (>200ms)?
                    ├─ YES → Phase 3F: Interaction optimization
                    └─ NO → ✅ Performance is good!

Resource Impact Scoring

Rate each resource by impact:

Factor Weight Score 1 Score 3 Score 5
Size (KB) 3x <10 10-100 >100
Render-blocking 5x No Partial Full
Above-fold impact 4x None Indirect Direct
Cacheable 2x Long cache Short cache No cache
Compressible 2x Already done Possible Not compressed

Priority = Sum(Factor × Weight). Fix highest scores first.

Phase 3: Fix Playbooks

3A: Server Optimization (TTFB)

Quick wins:

# CDN: If no CDN, this is #1 priority
# Check: curl -sI {url} | grep -i 'x-cache|cf-cache|x-cdn'

# Compression: Must have brotli or gzip
# Check: curl -sI -H "Accept-Encoding: br,gzip" {url} | grep -i content-encoding

# HTTP/2 or HTTP/3
# Check: curl -sI --http2 {url} | head -1

Server-side checklist: - [ ] CDN in front (Cloudflare, Fastly, CloudFront) - [ ] Brotli compression enabled (20-30% smaller than gzip) - [ ] HTTP/2 minimum, HTTP/3 if possible - [ ] Server-side caching (Redis, Varnish) - [ ] Database query optimization (<50ms per query) - [ ] Connection pooling enabled - [ ] Edge computing for dynamic content (Workers, Lambda@Edge)

Cache headers template:

# Static assets (CSS, JS, images, fonts)
Cache-Control: public, max-age=31536000, immutable

# HTML pages
Cache-Control: public, max-age=0, must-revalidate

# API responses
Cache-Control: private, max-age=60, stale-while-revalidate=300

3B: Render-Blocking Resources (FCP)

CSS optimization:

<!-- BEFORE: Render-blocking -->
<link rel="stylesheet" href="styles.css">

<!-- AFTER: Critical CSS inline + async load -->
<style>/* Critical above-fold CSS here (< 14KB) */</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

Rules: - Inline critical CSS (above-fold styles, < 14KB) - Defer non-critical CSS - Remove unused CSS (typical savings: 60-90%) - Combine media queries - Avoid @import (creates sequential loading)

JavaScript optimization:

<!-- BEFORE: Render-blocking -->
<script src="app.js"></script>

<!-- AFTER: Non-blocking -->
<script src="app.js" defer></script>

<!-- OR: Independent scripts -->
<script src="analytics.js" async></script>

Rules: - defer for app scripts (maintains order, runs after parse) - async for independent scripts (analytics, ads) - Never put <script> in <head> without defer/async - Inline small scripts (< 1KB)

3C: Hero Element Optimization (LCP)

LCP element types and fixes:

LCP Element Fix
<img> Preload + responsive + modern format
<video> poster Preload poster image
CSS background-image Preload + inline critical CSS
Text block Preload font + font-display: optional

Image optimization checklist:

<!-- Optimal hero image -->
<link rel="preload" as="image" href="hero.webp" 
      imagesrcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
      imagesizes="100vw">

<img src="hero.webp" 
     srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
     sizes="100vw"
     width="1200" height="600"
     alt="Hero description"
     fetchpriority="high"
     decoding="async">

Image format decision:

Photo/complex image? → WebP (25-35% smaller than JPEG)
                     → AVIF (50% smaller, but slower encode)
Simple graphic/logo? → SVG (scalable, tiny)
                    → PNG only if transparency needed
Animation?          → WebM/MP4 video (not GIF — 90% smaller)

Image size targets: | Viewport | Max width | Target KB | |----------|-----------|-----------| | Mobile | 400px | < 50KB | | Tablet | 800px | < 100KB | | Desktop | 1200px | < 150KB | | Hero/banner | 1600px | < 200KB |

3D: JavaScript Optimization (TBT)

Bundle analysis approach: 1. Check total JS size: web_fetch the page, count <script> tags 2. Identify large libraries (React, Lodash, Moment.js) 3. Check for duplicate code across bundles 4. Identify unused exports

Common JS bloat and replacements:

Library Size Alternative Size
moment.js 67KB date-fns 2-10KB
lodash (full) 71KB lodash-es (tree-shake) 2-5KB
jQuery 87KB vanilla JS 0KB
animate.css 80KB CSS animations 1-2KB
chart.js 60KB lightweight-charts 40KB

Code splitting rules: - Route-based splitting (each page loads its own JS) - Component-level splitting for heavy components (modals, editors, charts) - Dynamic import for below-fold features: const Chart = lazy(() => import('./Chart')) - Vendor chunk for stable dependencies (changes rarely = long cache)

Long task breaking:

// BEFORE: Blocks main thread 200ms+
function processLargeList(items) {
  items.forEach(item => heavyComputation(item));
}

// AFTER: Yields to main thread
async function processLargeList(items) {
  for (const item of items) {
    heavyComputation(item);
    // Yield every 50ms
    if (performance.now() - start > 50) {
      await scheduler.yield(); // or setTimeout(0)
      start = performance.now();
    }
  }
}

3E: Layout Stability (CLS)

Top CLS causes and fixes:

Cause Fix
Images without dimensions Always set width + height
Ads/embeds without space Reserve space with aspect-ratio or min-height
Dynamic content injection Use CSS contain or reserved space
Web fonts causing reflow font-display: optional or swap with size-adjust
Late-loading CSS Inline critical CSS

Anti-CLS patterns:

/* Reserve space for dynamic content */
.ad-slot { min-height: 250px; }
.embed-container { aspect-ratio: 16/9; }

/* Prevent font swap reflow */
@font-face {
  font-family: 'Brand';
  src: url('brand.woff2') format('woff2');
  font-display: optional; /* No swap = no shift */
  size-adjust: 105%; /* Match fallback metrics */
}

/* Contain layout shifts */
.dynamic-widget {
  contain: layout;
  min-height: 200px;
}

3F: Interaction Optimization (INP)

Event handler rules: - Keep handlers < 50ms - Debounce scroll/resize (100-150ms) - Use requestAnimationFrame for visual updates - Offload heavy computation to Web Workers - Use content-visibility: auto for off-screen content

Input responsiveness:

// BEFORE: Blocks during type
input.addEventListener('input', (e) => {
  expensiveFilter(e.target.value); // 100ms+ 
});

// AFTER: Debounced + visual feedback
input.addEventListener('input', (e) => {
  showSpinner(); // Instant visual feedback
  debounce(() => expensiveFilter(e.target.value), 150);
});

Phase 4: Resource Loading Strategy

Preload / Prefetch / Preconnect Decision

<!-- Preconnect: Third-party origins you'll need soon -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>

<!-- DNS-prefetch: Third-party origins you might need -->
<link rel="dns-prefetch" href="https://analytics.example.com">

<!-- Preload: Critical resources for THIS page -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="hero.webp" as="image">
<link rel="preload" href="brand.woff2" as="font" type="font/woff2" crossorigin>

<!-- Prefetch: Resources for NEXT page (low priority) -->
<link rel="prefetch" href="/next-page.js">

<!-- Modulepreload: ES modules -->
<link rel="modulepreload" href="app.mjs">

Rules: - Max 3-5 preloads per page (more = competing priorities) - Always preload: LCP image, critical font, above-fold CSS - Preconnect to known third-party origins (max 4-6) - Prefetch only on fast connections

Lazy Loading Strategy

Above fold (viewport):     fetchpriority="high", no lazy
Below fold (1-2 screens):  loading="lazy", decoding="async"
Way below fold:            Intersection Observer, load on demand
Off-screen widgets:        content-visibility: auto

Font Loading Optimization

/* Optimal font loading */
@font-face {
  font-family: 'Brand';
  src: url('brand.woff2') format('woff2');
  font-display: swap;
  unicode-range: U+0000-00FF; /* Latin only if applicable */
}

Font checklist: - [ ] WOFF2 format only (best compression) - [ ] Subset fonts (Latin, extended only if needed) - [ ] Max 2-3 font families - [ ] Max 4 font files total (regular, bold, italic, bold-italic) - [ ] Preload critical font files - [ ] Consider system font stack for body text

System font stacks:

/* Modern system fonts — zero network cost */
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

/* Monospace */
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, monospace;

Phase 5: Third-Party Script Management

Impact Assessment

third_party_audit:
  - script: "Google Analytics 4"
    size_kb: 45
    blocks_render: false
    loads_more_scripts: true
    total_impact_kb: 90
    essential: true
    mitigation: "gtag async, delay until interaction"

  - script: "Intercom chat widget"
    size_kb: 200
    blocks_render: false
    loads_more_scripts: true
    total_impact_kb: 450
    essential: false
    mitigation: "Load on scroll/click, not page load"

Third-party loading strategies:

// Strategy 1: Load on interaction
document.addEventListener('scroll', () => {
  loadThirdParty('chat-widget.js');
}, { once: true });

// Strategy 2: Load after page is idle
requestIdleCallback(() => {
  loadThirdParty('analytics.js');
});

// Strategy 3: Facade pattern (show placeholder until needed)
chatButton.addEventListener('click', () => {
  loadThirdParty('intercom.js').then(() => Intercom('show'));
});

Rules: - Audit ALL third-party scripts quarterly - Every script needs a business justification - If a script loads >100KB, it needs a loading strategy - Self-host what you can (fonts, analytics alternatives) - Use rel="noopener" on all external links

Phase 6: Mobile Performance

Mobile-Specific Optimization

Targets (mobile on 4G): | Metric | Good | Needs Work | Poor | |--------|------|------------|------| | FCP | < 1.8s | 1.8-3.0s | > 3.0s | | LCP | < 2.5s | 2.5-4.0s | > 4.0s | | TBT | < 200ms | 200-600ms | > 600ms | | CLS | < 0.1 | 0.1-0.25 | > 0.25 | | INP | < 200ms | 200-500ms | > 500ms |

Mobile-specific checklist: - [ ] Viewport meta tag present - [ ] Touch targets ≥ 48×48px - [ ] No horizontal scroll - [ ] Images responsive (srcset + sizes) - [ ] JS budget < 300KB (compressed) on mobile - [ ] Critical CSS < 14KB (fits in first TCP round trip) - [ ] Avoid complex CSS (heavy animations, large box-shadows)

Phase 7: Performance Budget

Setting Budgets

performance_budget:
  metrics:
    lcp_ms: 2500
    fcp_ms: 1800
    tbt_ms: 200
    cls: 0.1
    inp_ms: 200

  resources:
    total_kb: 1500
    js_kb: 350
    css_kb: 80
    images_kb: 800
    fonts_kb: 100

  requests:
    total: 60
    third_party: 15

  lighthouse:
    performance: 90
    accessibility: 90
    best_practices: 90
    seo: 90

Budget enforcement rules: - Any PR that increases JS by >10KB needs justification - LCP regression > 200ms blocks deploy - Monthly Lighthouse audit — track trend - Per-route budgets for SPAs (homepage stricter than admin)

Budget Monitoring Template

# Weekly performance check
date: "YYYY-MM-DD"
url: ""
device: "mobile"

scores:
  lighthouse: null
  lcp: null
  fcp: null
  tbt: null
  cls: null

trend: "improving | stable | degrading"
regressions: []
actions: []

Phase 8: Performance Scoring Rubric

Rate the site 0-100:

Dimension Weight 0-2 3-4 5
Core Web Vitals 25% All red Mixed All green
Page weight 15% >5MB 2-5MB <2MB
Caching strategy 15% None Partial Full with immutable
Render path 15% Multiple blockers Some optimized Clean critical path
Image optimization 10% Unoptimized Partially WebP/AVIF + responsive
JavaScript health 10% >1MB, no splitting Some splitting <350KB, code-split
Third-party control 5% Unmanaged Some deferred All managed + budgeted
Mobile experience 5% Desktop-only Responsive Mobile-first optimized

Score interpretation: - 90-100: Elite. Maintain and iterate. - 70-89: Good. Fix the weakest dimension. - 50-69: Needs work. Follow Phase 3 playbooks. - <50: Critical. Start with server + render-blocking fixes.

Phase 9: Common Architectures — Quick Wins

Next.js / React

  • Use next/image (auto WebP, lazy, blur placeholder)
  • Enable ISR or SSG for static pages
  • Use dynamic() for heavy components
  • Check bundle with @next/bundle-analyzer
  • Middleware for edge caching

WordPress

  • Page cache plugin (WP Super Cache, W3 Total Cache)
  • Image optimization (ShortPixel, Imagify)
  • Disable unused plugins (each adds JS+CSS)
  • Use a CDN plugin
  • Consider static generation (Simply Static)

SPA (React/Vue/Svelte)

  • Route-based code splitting (mandatory)
  • SSR or SSG for SEO pages
  • Service worker for repeat visits
  • Skeleton screens (not spinners)
  • Virtual scrolling for long lists

Static Sites

  • Already fast — focus on image optimization
  • Deploy to CDN edge (Cloudflare Pages, Netlify, Vercel)
  • Inline all critical CSS
  • Minimal JS (< 50KB)

Phase 10: Advanced Techniques

Service Worker Caching

// Cache-first for static assets
self.addEventListener('fetch', (event) => {
  if (event.request.url.match(/.(css|js|woff2|webp|avif)$/)) {
    event.respondWith(
      caches.match(event.request).then(cached => cached || fetch(event.request))
    );
  }
});

Resource Hints for Navigation

// Predictive prefetch on hover
document.querySelectorAll('a').forEach(link => {
  link.addEventListener('mouseenter', () => {
    const prefetch = document.createElement('link');
    prefetch.rel = 'prefetch';
    prefetch.href = link.href;
    document.head.appendChild(prefetch);
  }, { once: true });
});

Performance Monitoring in Production

// Report Core Web Vitals
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Send to analytics
    sendToAnalytics({
      metric: entry.name,
      value: entry.value,
      rating: entry.rating, // "good" | "needs-improvement" | "poor"
    });
  }
}).observe({ type: 'largest-contentful-paint', buffered: true });

Edge Cases

Infinite scroll / pagination: - Virtual scrolling for >100 items - Intersection Observer to load batches - content-visibility: auto for off-screen items - Memory management: remove far-off-screen DOM nodes

SPAs with client-side routing: - Measure soft navigations (not just initial load) - Report per-route metrics - Prefetch likely next routes - Keep route JS < 100KB each

E-commerce product pages: - Preload first product image - Lazy load review section, related products - Defer recommendation engine JS - Cache product data with stale-while-revalidate

Media-heavy sites: - Lazy load everything below fold - Use <video> not GIF (90% smaller) - Adaptive quality based on connection (Network Information API) - Progressive JPEG for large photos

Natural Language Commands

  • "Audit {url}" → Run full Phase 1 audit
  • "Fix LCP on {url}" → Phase 3C playbook
  • "What's slowing down {url}?" → Phase 2 diagnosis tree
  • "Set performance budget for {project}" → Phase 7 template
  • "Score {url}" → Phase 8 rubric
  • "Optimize images on {url}" → Phase 3C image checklist
  • "Reduce JavaScript on {url}" → Phase 3D JS optimization
  • "Fix layout shifts on {url}" → Phase 3E CLS playbook
  • "Mobile performance audit for {url}" → Phase 6
  • "Third-party script audit for {url}" → Phase 5
  • "Weekly performance check for {url}" → Phase 7 monitoring template
  • "Compare {url1} vs {url2}" → Side-by-side audit