jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Web Performance · Part 12 — Capstone 1: Audit, Budget & CI Monitoring

Combine all 11 parts into a real A–Z audit workflow, set concrete performance budgets, and block regressions with Lighthouse CI plus RUM alerts. Includes a full checklist.

Mười một phần trước cho bạn từng kỹ thuật. Capstone nền tảng này biến chúng thành một quy trình lặp lại: audit một trang, sửa theo thứ tự ưu tiên, đặt budget để giữ thành quả, và tự động chặn hồi quy trong CI. Đây là cách hiệu năng trở thành kỷ luật, không phải đợt “tổng vệ sinh” một lần rồi trôi.


1. Quy trình audit một trang thật

Theo đúng tinh thần “đo trước, sửa sau” (Phần 1):

Bước 1 — Đo field trước (có thật sự là vấn đề?)

Mở CrUX/PageSpeed Insights hoặc RUM (Phần 3). Trang này có trượt CWV với người dùng thật không? Chỉ số nào (LCP/INP/CLS), ở route và phân khúc thiết bị/mạng nào? Đánh giá ngưỡng ở p75 của từng phân khúc đủ mẫu. Đây xác định có nên sửasửa cái gì.

Bước 2 — Tái hiện & chẩn đoán bằng lab

Chạy Lighthouse + tab Performance với một cấu hình lab lặp lại, ưu tiên thiết bị đại diện hoặc CPU throttle đã hiệu chỉnh; 4× + Slow 4G chỉ là điểm bắt đầu, không phải cấu hình đúng cho mọi máy/người dùng (Phần 3). Đọc:

  • LCP cao → phần tử LCP là gì? Thành phần nào lớn (TTFB / resource load delay / resource load duration / element render delay)? (Phần 5)
  • INP cao → long task nào chặn main thread? (Phần 6)
  • CLS cao → node nào nhảy (xem sources)? (Phần 7)

Bước 3 — Sửa theo thứ tự ROI

Sửa bottleneck lớn nhất trước. Bản đồ “triệu chứng → phần”:

Triệu chứngXem phần
High TTFB5 (CDN/cache/SSG), 11
Slow LCP image5, 9 (format, discovery, fetchpriority; preload nếu bị phát hiện trễ)
Long task / INP6 (yield, worker), 8 (bundle)
Layout shift7 (dimensions, size-adjust), 10 (font)
JS too heavy8 (split, tree shake)
Reload still slow11 (caching)

Bước 4 — Đo lại & xác nhận

Sau mỗi thay đổi, đo lại (lab để xác nhận cơ chế, field để xác nhận tác động thật). Đừng gộp 5 thay đổi rồi đo một lần — bạn sẽ không biết cái nào hiệu quả.


2. Performance Budget: chốt ngưỡng để không trôi

Tối ưu xong mà không có budget thì vài sprint sau lại chậm (ai đó thêm thư viện nặng). Budget là ngưỡng bạn cam kết không vượt. Ba loại:

Loại budgetVí dụ ngưỡng
MetricLCP < 2.5s, INP < 200ms, CLS < 0.1
SizeJS ≤ 170KB (gzip), total images ≤ 500KB/page
Count≤ 50 requests, ≤ 1 critical font file

Khai báo budget cho công cụ tự kiểm:

Tạo budget.json (kích thước tính bằng KiB, thời gian tính bằng ms; CLS là điểm không đơn vị), rồi truyền file này cho Lighthouse/Lighthouse CI trong cấu hình:

[
  {
    "path": "/*",
    "resourceSizes": [
      { "resourceType": "script", "budget": 170 },
      { "resourceType": "image", "budget": 500 },
      { "resourceType": "total", "budget": 1000 }
    ],
    "timings": [
      { "metric": "largest-contentful-paint", "budget": 2500 },
      { "metric": "cumulative-layout-shift", "budget": 0.1 }
    ]
  }
]

3. Lighthouse CI: chặn hồi quy tự động

Đặt budget rồi phải có ai đó gác cổng. Lighthouse CI chạy trong pipeline, fail build nếu vượt budget — biến hiệu năng thành một phần của code review.

# .github/workflows/perf.yml
name: Performance
on: [pull_request]
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: npm
      - run: npm ci && npm run build
      - name: Lighthouse CI
        run: npm exec -- lhci autorun

Cài @lhci/cli vào devDependencies và pin qua lockfile, thay vì cài global phiên bản mới nhất trong mỗi lần CI.

// lighthouserc.cjs — threshold "assert" configuration
module.exports = {
  ci: {
    collect: {
      staticDistDir: './dist',
      numberOfRuns: 3,
    },
    assert: {
      assertions: {
        // LHCI native size assertions dùng byte (budget.json ở trên dùng KiB).
        'resource-summary:script:size': ['error', { maxNumericValue: 170 * 1024 }],
        'resource-summary:image:size': ['error', { maxNumericValue: 500 * 1024 }],
        'resource-summary:total:size': ['error', { maxNumericValue: 1000 * 1024 }],
        'categories:performance': ['warn', { minScore: 0.9, aggregationMethod: 'median' }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500, aggregationMethod: 'median' }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1, aggregationMethod: 'median' }],
        'total-blocking-time': ['warn', { maxNumericValue: 300, aggregationMethod: 'median' }], // proxy lab, không phải INP
      },
    },
  },
};

Ví dụ trên chuyển các resourceSizes sang assertion native của LHCI để có thể đặt cùng metric assertions. Nếu muốn dùng nguyên budget.json, thay toàn bộ cấu hình assert bằng assert: { budgetsFile: './budget.json' }; budgetsFile không được kết hợp với tùy chọn assert khác. Không dùng audit ID performance-budget: audit này không còn trong cấu hình Lighthouse hiện tại.

Lighthouse CI là lab — nó chặn hồi quy cơ chế (bundle phình, ảnh không tối ưu). Navigation run không tạo tương tác người dùng thật nên không thể thay INP field; TBT chỉ là tín hiệu chẩn đoán trong giai đoạn tải. Hãy ghép với cảnh báo RUM và, nếu cần, user flow có tương tác được script hóa.


4. Giám sát RUM liên tục (field)

Lab chặn hồi quy lúc build; RUM (Phần 3) cho bạn biết thực tế người dùng sau khi deploy. Thiết lập:

  • Thu LCP/INP/CLS qua web-vitals → gửi sendBeacon về backend.
  • Dashboard tính p75 theo route + thiết bị.
  • Cảnh báo khi p75 một chỉ số vượt ngưỡng (ví dụ INP p75 > 200ms ở route checkout) → điều tra ngay.

Đây khép kín vòng lặp: budget + Lighthouse CI ngăn hồi quy trước khi lên production; RUM bắt vấn đề thật mà lab không thấy (thiết bị/mạng đa dạng).


5. Checklist hiệu năng tổng hợp (chặng nền tảng)

Mang theo khi audit bất kỳ trang nào:

Đo lường

  1. Có field data (CrUX/RUM) xác định vấn đề & route chưa? (Phần 1, 3)
  2. Đã profile với thiết bị đại diện hoặc CPU/network throttle được hiệu chỉnh và ghi lại để tái lập? (Phần 3)

Loading & LCP

  1. TTFB thấp (CDN/cache/SSG)? (Phần 5, 11)
  2. Ảnh LCP: URL discoverable sớm trong HTML, không lazy; dùng fetchpriority="high" cho ứng viên rõ ràng và chỉ preload khi bị phát hiện trễ? (Phần 5, 9)
  3. CSS critical inline, JS defer/split? (Phần 4, 8)

Interactivity & INP

  1. Không còn long task lớn (yield/worker)? (Phần 6)
  2. Bundle đã đo, split theo route, tree-shake? (Phần 8)

Stability & CLS

  1. Ảnh/video/iframe có kích thước; nội dung động chừa chỗ? (Phần 7, 9)
  2. Font: woff2, chiến lược swap/optional phù hợp, preload chỉ font critical, và cân chỉnh fallback (size-adjust) khi cần? (Phần 10)

Network

  1. Asset hash cache immutable, HTML revalidate, content encoding được negotiate (br/zstd với fallback gzip), và HTTP/2-3 đã được kiểm tra trên hạ tầng thật? (Phần 11)

Kỷ luật

  1. Có performance budget + Lighthouse CI + cảnh báo RUM? (bài này)

Kết chặng nền tảng

Hiệu năng web không phải phép thuật, cũng không phải một lần “tối ưu cho xong”. Nó là một vòng lặp kỷ luật: đo người dùng thật → chẩn đoán bằng lab → sửa bottleneck lớn nhất → chốt bằng budget → tự động gác cổng trong CI → giám sát lại bằng RUM. Mỗi kỹ thuật trong 12 phần nền tảng chỉ là công cụ; điều biến bạn thành kỹ sư hiệu năng là quy trình dùng chúng.

Hãy chọn một trang quan trọng nhất trong sản phẩm của bạn, chạy quy trình audit ở mục 1, và sửa đúng một bottleneck lớn nhất. Đo lại, thấy con số cải thiện — đó là cách kiến thức này thành phản xạ.

Phần tiếp theo: RUM & Field Data Deep Dive mở chặng chuyên sâu 13–22: attribution, lấy mẫu, phân khúc và CrUX để điều tra vấn đề trên người dùng thật.