jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Web Performance · Part 1 — Mental Model & the RAIL Framework

Opening the series: why performance is a feature, perceived vs actual performance, the RAIL model with human-perception time thresholds, and the "measure first, fix later" principle.

Người dùng không quan tâm app của bạn dùng framework gì. Họ quan tâm nó mở nhanh không, bấm có phản hồi không, có nhảy lung tung không. Hiệu năng không phải thứ “tối ưu sau” — nó là một tính năng, ảnh hưởng trực tiếp tới tỉ lệ rời trang, chuyển đổi và cả SEO (Google dùng Core Web Vitals làm tín hiệu xếp hạng).

Series này gồm 22 phần, chia thành hai chặng: Phần 1–12 xây nền tảng và kết thúc bằng một bài audit; Phần 13–22 đi sâu vào RUM, kiến trúc render, scheduling, protocol, third-party và điều tra end-to-end. Bắt đầu từ tư duy nền tảng.

Chặng nền tảng (Phần 1–12):

PartChủ đề
1Tư duy nền tảng & RAIL (bài này)
2Core Web Vitals: LCP, INP, CLS
3Công cụ đo
4Critical rendering path & loading
5Tối ưu LCP
6Tối ưu INP
7Tối ưu CLS
8JavaScript performance & bundle
9Ảnh & media
10Fonts
11Network & caching
12Capstone: audit & budget

Chặng chuyên sâu (Phần 13–22):

PartChủ đề
13RUM & field data chuyên sâu
14Resource hints, priorities & Early Hints
15Chiến lược render & delivery
16INP chuyên sâu & main-thread scheduling
17Rendering pipeline & CSS performance
18Memory & hiệu năng phiên dài
19Network protocol & transport
20Third-party script performance
21Budgets, automation & performance culture
22Capstone 2: điều tra hiệu năng có hệ thống

1. Hiệu năng cảm nhận vs hiệu năng thực

Có hai loại “nhanh”:

  • Hiệu năng thực (actual): con số đo được — bao nhiêu mili-giây để tải, để phản hồi.
  • Hiệu năng cảm nhận (perceived): cảm giác của người dùng — nhiều khi quan trọng hơn con số.

Một trang hiện skeleton ngay rồi load dần cảm giác nhanh hơn một trang màn hình trắng 2 giây rồi hiện hết, dù tổng thời gian bằng nhau. Đây là lý do mọi kỹ thuật như skeleton screen, progressive loading, optimistic UI tồn tại: chúng cải thiện cảm nhận ngay cả khi không đổi con số thực.

Bài học: tối ưu hiệu năng không chỉ là giảm mili-giây, mà còn là quản lý cảm nhận — cho người dùng tín hiệu rằng “mọi thứ đang chạy”.


2. Các ngưỡng thời gian con người cảm nhận

Việc tối ưu phải neo vào cách con người cảm nhận thời gian. Có vài mốc kinh điển:

Thời gianCảm nhận của người dùng
0–100msTức thì — như thao tác vật lý trực tiếp
100–300msCó độ trễ nhẹ nhưng tương tác vẫn liền mạch
300ms–1sĐộ trễ đã dễ nhận thấy; nên có phản hồi tiến trình nếu tác vụ còn tiếp tục
> 1sMất tập trung; người dùng bắt đầu sốt ruột
> 10sBỏ cuộc; cần thanh tiến trình rõ ràng

Đây là các mốc cảm nhận mang tính định hướng, không phải SLA cứng cho mọi ngữ cảnh. Chúng tạo nền cho RAIL; còn ngưỡng Core Web Vitals được Google xác định riêng, cân bằng giữa chất lượng trải nghiệm và khả năng đạt được trên web thực tế. Ngày nay, Core Web Vitals là bộ mục tiêu nên ưu tiên; RAIL vẫn hữu ích như mô hình phân rã công việc theo ngữ cảnh.


3. Mô hình RAIL

RAIL là khung tư duy của Google chia trải nghiệm thành 4 khía cạnh, mỗi cái có mục tiêu thời gian riêng. Nó giúp bạn biết “nhanh đủ” là bao nhiêu cho từng loại tương tác.

R — Response

Hiển thị phản hồi cho sự kiện người dùng trong dưới 100ms. Bấm nút, gõ phím, tick checkbox — phải có phản hồi gần như tức thì để cảm giác “trực tiếp”. Để còn ngân sách cho input delay và render, phần xử lý sự kiện thường nên nằm trong khoảng 50ms.

Nếu việc xử lý cần lâu hơn, hãy phản hồi ngay (đổi trạng thái nút, hiện spinner) rồi làm việc nặng ở nền — tách “phản hồi” khỏi “hoàn thành”.

A — Animation

Ở màn hình 60Hz, mỗi khung hình có khoảng 16.7ms; ứng dụng thường chỉ còn khoảng 10ms vì trình duyệt cũng cần style, layout và paint. Màn hình có tần số quét cao còn có ngân sách thấp hơn. Đây là lý do animation ưu tiên transform/opacity và luôn phải đo trên thiết bị đích.

I — Idle

Dồn việc không gấp vào lúc trình duyệt rảnh, chia thành khối ~50ms để luôn sẵn sàng phản hồi nếu người dùng tương tác.

L — Load

Trang nên tương tác được trong vài giây đầu trên mạng/máy tầm trung. Mục tiêu kinh điển: nội dung chính hiển thị nhanh, có thể bấm được sớm.


4. Field data vs Lab data — phân biệt sống còn

Đây là khái niệm khiến nhiều người đo sai và “tối ưu nhầm”:

  • Lab data (đo trong phòng thí nghiệm): chạy trên môi trường mô phỏng cố định (Lighthouse trên máy bạn). Lặp lại được, hợp để debug. Nhưng không phản ánh người dùng thật.
  • Field data (đo ngoài thực địa): thu từ người dùng thật, máy thật, mạng thật (CrUX, RUM). Đây là dữ liệu quan trọng để đánh giá trải nghiệm thực; CWV field là một trong các tín hiệu trải nghiệm trang của Google Search.

Lab nói “trang của tôi nhanh trên MacBook của tôi”. Field nói “trang của tôi nhanh với người dùng thật dùng điện thoại 4G”. Hai con số có thể khác nhau rất xa. Luôn ưu tiên field data để quyết định, dùng lab data để chẩn đoán.

Ta sẽ đào sâu cách thu cả hai ở Phần 3.


5. Nguyên tắc: đo trước, sửa sau

Giống series thuật toán, quy tắc số một là đừng tối ưu mò. Bottleneck hiệu năng rất hay phản trực giác — thứ bạn nghĩ chậm thường không phải thứ chậm thật.

Quy trình tối ưu đúng:

  1. Đo trên môi trường giống người dùng (mạng chậm, CPU throttle) — không phải MacBook cắm điện.
  2. Tìm bottleneck lớn nhất — quy luật 80/20: thường một vài thứ chiếm phần lớn thời gian.
  3. Sửa cái lớn nhất trước, đo lại để xác nhận có cải thiện thật.
  4. Đặt budget để không bị “trôi” hiệu năng theo thời gian (Phần 12).

Một mẹo thiết lập đo cho đúng: trong DevTools, chọn CPU/network throttling đại diện cho nhóm người dùng mục tiêu (ví dụ điện thoại tầm trung và mạng di động), rồi giữ cấu hình đó ổn định khi so sánh trước/sau. Preset chỉ là mô phỏng; hãy hiệu chỉnh bằng field data thay vì mặc định một cấu hình cho mọi sản phẩm.


6. Đo nhanh ngay trong code: Web Performance API

Trình duyệt cho bạn đo trực tiếp. PerformanceObserver là cách chuẩn để bắt các mốc hiệu năng (ta dùng nhiều ở các phần sau):

// Observe "paint" events like First Contentful Paint
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`${entry.name}: ${entry.startTime.toFixed(0)}ms`);
  }
});
observer.observe({ type: 'paint', buffered: true }); // buffered: also fetch entries that already fired
// Measure synchronous handler work; this does NOT include the next paint.
button.addEventListener('click', () => {
  const start = performance.now();
  handleClick();
  const ms = performance.now() - start;
  if (ms > 50) console.warn(`Long handler: ${ms.toFixed(0)}ms (> 50ms)`);
});

Đo thời gian từ input tới khung hình kế tiếp (INP) phức tạp hơn phép đo handler này; Phần 2 dùng Event Timing và thư viện web-vitals cho đúng định nghĩa.


Tóm tắt

  • Hiệu năng là tính năng, ảnh hưởng chuyển đổi và SEO; cần phân biệt hiệu năng thựccảm nhận.
  • Neo tối ưu vào ngưỡng cảm nhận của con người (100ms tức thì, 1s mất tập trung).
  • RAIL: Response hiển thị < 100ms (handler thường ≤ 50ms), Animation theo ngân sách frame, Idle chia khối ≤ 50ms, Load nhanh và sớm tương tác được.
  • Field data (người dùng thật) quan trọng hơn lab data (phòng thí nghiệm) cho việc ra quyết định.
  • Đo trước, sửa sau; dùng CPU/mạng đại diện và nhất quán khi profile.

Phần tiếp theo: Core Web Vitals — LCP, INP, CLS: chính xác chúng đo gì, ngưỡng nào là “tốt”, và đo ra sao.