Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Browser-based performance — Lighthouse, WebPageTest

Mở đầu — vì sao bài này quan trọng

Suốt gần 50 bài của khóa học này, chúng ta đã đo áp lực lên server: bao nhiêu request mỗi giây, latency ở p95 là bao nhiêu, error rate tăng lên lúc nào. JMeter và k6 giúp ta trả lời câu hỏi "server chịu được bao nhiêu người?". Nhưng có một sự thật phũ phàng mà rất nhiều đội QA bỏ quên: server nhanh không có nghĩa là người dùng thấy nhanh.

Hãy tưởng tượng bạn test API /api/products bằng k6, thấy p95 latency chỉ 80ms — quá tốt. Bạn báo cáo "hệ thống ổn". Nhưng khi khách hàng thật mở trang sản phẩm trên điện thoại Android tầm trung ở Cần Thơ với mạng 4G chập chờn, họ phải chờ 6 giây mới thấy được nút "Mua ngay". API trả về trong 80ms, nhưng trình duyệt còn phải tải 2MB JavaScript, render lại layout ba lần, chờ font chữ, chờ ảnh, chờ script quảng cáo bên thứ ba... Toàn bộ phần "last mile" — chặng cuối cùng từ khi byte đầu tiên rời server đến khi mắt người dùng thấy nội dung dùng được — nằm ở trình duyệt, và JMeter/k6 hoàn toàn không nhìn thấy chặng này.

Đây chính là địa hạt của browser-based performance testing (đo hiệu năng phía trình duyệt). Bài này giới thiệu hai công cụ nền tảng của cả ngành: Lighthouse (do Google phát triển, tích hợp sẵn trong Chrome) và WebPageTest (công cụ đo chuyên sâu, mô phỏng thiết bị và mạng thật). Chúng ta cũng sẽ nắm vững bộ chỉ số Core Web Vitals — thứ mà Google dùng để xếp hạng SEO, và cũng là thứ trực tiếp quyết định tỷ lệ chuyển đổi (conversion rate) của website. Nắm được browser performance, bạn không chỉ là người "test tải server" nữa, mà trở thành người bảo vệ trải nghiệm thật của khách hàng.

Khái niệm cốt lõi

Backend performance vs. Frontend performance

Cần phân biệt rạch ròi hai khái niệm:

  • Backend/server-side performance: thời gian server xử lý và trả về response. Đây là thứ JMeter/k6 đo. Chỉ số tiêu biểu: TTFB (Time To First Byte).
  • Frontend/browser-side performance: thời gian trình duyệt tải, phân tích (parse), thực thi và render nội dung cho đến khi người dùng thực sự tương tác được. Đây là thứ Lighthouse/WebPageTest đo.
Một trang có thể có TTFB tuyệt vời (server nhanh) nhưng vẫn "chậm" với người dùng vì frontend cồng kềnh. Ngược lại cũng vậy. Người kỹ sư hiệu năng giỏi phải nhìn cả hai đầu.

Core Web Vitals — bộ ba chỉ số của Google

Google chuẩn hóa trải nghiệm tải trang thành ba trụ cột, mỗi trụ đại diện cho một khía cạnh cảm nhận của người dùng. Ngưỡng đánh giá (2026):

MetricÝ nghĩaGoodCần cải thiệnPoor
LCP (Largest Contentful Paint)Khi nào phần tử nội dung lớn nhất (ảnh hero, tiêu đề) hiện ra — đo "trang đã tải xong chưa?"≤ 2.5s2.5s–4.0s> 4.0s
INP (Interaction to Next Paint)Độ trễ khi người dùng bấm/gõ đến khi màn hình phản hồi — đo "trang có mượt không?"≤ 200ms200ms–500ms> 500ms
CLS (Cumulative Layout Shift)Mức độ layout nhảy loạn khi tải (nút bấm bị đẩy đi chỗ khác) — đo "trang có ổn định không?"≤ 0.10.1–0.25> 0.25
Lưu ý quan trọng: từ tháng 3/2024, INP đã chính thức thay thế FID (First Input Delay) làm một trong ba Core Web Vitals. Nếu bạn đọc tài liệu cũ nhắc đến FID thì nó đã lỗi thời. INP khắt khe hơn nhiều vì nó đo mọi tương tác trong suốt phiên, không chỉ tương tác đầu tiên.

Ngoài ba chỉ số chính, còn vài chỉ số bổ trợ hay gặp trong báo cáo Lighthouse:

  • FCP (First Contentful Paint): khi pixel đầu tiên có nội dung hiện ra.
  • TBT (Total Blocking Time): tổng thời gian luồng chính (main thread) bị JavaScript chặn — chỉ số này trong phòng lab thường tương quan mạnh với INP thực địa.
  • Speed Index: tốc độ nội dung phía trên màn hình được lấp đầy.
  • TTFB: cầu nối duy nhất giữa thế giới backend và frontend.

Lab data vs. Field data — sự khác biệt sống còn

Đây là điểm nhiều người mới hiểu sai và báo cáo sai:

  • Lab data (dữ liệu phòng lab): đo trong môi trường mô phỏng, có kiểm soát — như khi bạn chạy Lighthouse trên máy mình. Ưu điểm: lặp lại được, tốt để debug. Nhược điểm: không phản ánh sự đa dạng của thiết bị/mạng người dùng thật.
  • Field data / RUM (Real User Monitoring — giám sát người dùng thật): thu thập từ chính người dùng thật đang truy cập. Nguồn phổ biến nhất là CrUX (Chrome User Experience Report) — kho dữ liệu Google gom từ hàng triệu người dùng Chrome thật. Đây mới là dữ liệu Google dùng để xếp hạng SEO.
Nguyên tắc vàng: dùng lab data để chẩn đoán, dùng field data để đánh giá. Một trang có thể đạt điểm Lighthouse 95 trên máy MacBook của bạn nhưng CrUX vẫn báo "Poor" vì phần lớn khách của bạn dùng điện thoại giá rẻ trên mạng 3G.

Lighthouse và WebPageTest — hai công cụ, hai vai trò

  • Lighthouse: nhanh, miễn phí, tích hợp sẵn trong Chrome DevTools (tab Lighthouse), chạy được qua CLI, qua PageSpeed Insights (kèm luôn field data từ CrUX). Cho điểm 0–100 và danh sách khuyến nghị cụ thể. Đây là công cụ "hàng ngày".
  • WebPageTest: chuyên sâu hơn, mô phỏng thiết bị và điều kiện mạng thật (chọn được "Samsung Galaxy A ở Việt Nam, mạng 4G"), chạy nhiều lần lấy trung vị, cho ra waterfall chart (biểu đồ thác nước) chi tiết từng request, filmstrip (dải phim từng khung hình tải trang), so sánh side-by-side nhiều URL. Đây là công cụ "điều tra sâu".

Tình huống thực tế

Ví dụ 1 — Sàn thương mại điện tử thời trang: LCP giết chết conversion

Một startup thời trang online tại TP.HCM (giả định là "MặcĐẹp.vn") thấy doanh thu mobile thấp bất thường dù chi tiền quảng cáo mạnh. Đội backend khẳng định "API nhanh mà, k6 test p95 chỉ 120ms". Team QA quyết định chạy Lighthouse ở chế độ Mobile trên trang danh mục sản phẩm.

Kết quả gây sốc: LCP = 6.8 giây, điểm Performance chỉ 34/100. Waterfall trên WebPageTest chỉ ra thủ phạm: ảnh banner hero là file JPEG 3.2MB, tải nguyên kích thước desktop rồi mới bị CSS thu nhỏ trên mobile. Ngoài ra có 4 file JavaScript của công cụ chat và analytics chặn render.

Đội xử lý: nén và chuyển ảnh sang định dạng WebP (giảm còn 180KB), thêm thuộc tính fetchpriority="high" cho ảnh hero, và defer các script bên thứ ba. Sau hai tuần, LCP xuống 2.1 giây, và điều quan trọng: field data từ CrUX chuyển từ "Poor" sang "Good", tỷ lệ thêm-vào-giỏ trên mobile tăng 18%.

Bài học: đo server là chưa đủ. Một tấm ảnh nặng — thứ k6 không bao giờ thấy — có thể là điểm nghẽn conversion lớn nhất của cả doanh nghiệp.

Ví dụ 2 — Báo điện tử: CLS làm độc giả bấm nhầm quảng cáo

Một trang tin tức lớn (giả định "TinNhanh24h") nhận vô số phản hồi bực bội: độc giả đang đọc thì màn hình bỗng "nhảy", và họ bấm nhầm vào banner quảng cáo thay vì bài viết muốn đọc. Đây là dấu hiệu kinh điển của CLS cao.

Chạy Lighthouse, CLS đo được 0.42 (ngưỡng Poor là >0.25). Xem lại filmstrip trên WebPageTest, thấy rõ: các ô quảng cáo được nạp bất đồng bộ và không được đặt kích thước trước (không có width/height reserve). Khi quảng cáo về, nó chèn vào giữa nội dung và đẩy toàn bộ phần bên dưới nhảy xuống.

Giải pháp: đặt sẵn khung kích thước cố định (min-height) cho mọi vị trí quảng cáo trước khi nội dung quảng cáo tải về; thêm width/height cho mọi thẻ <img>. CLS giảm còn 0.05. Tỷ lệ bấm nhầm giảm mạnh, thời gian đọc trung bình mỗi phiên tăng lên.

Bài học: CLS không phải chỉ số "làm đẹp". Layout nhảy loạn trực tiếp phá hoại lòng tin và trải nghiệm — và với báo chí thì đó là tiền quảng cáo hợp lệ.

Ví dụ 3 — App ngân hàng số: INP tố cáo JavaScript quá tải

Một ngân hàng số ở Đông Nam Á ra mắt tính năng chuyển tiền mới. Người dùng phàn nàn: bấm nút "Xác nhận" xong phải chờ cả giây mới thấy màn hình phản hồi, tưởng app treo nên bấm lại nhiều lần (dẫn tới rủi ro tạo giao dịch trùng).

k6 test API /transfer cho thấy response 90ms — hoàn hảo. Nhưng vấn đề nằm ở trình duyệt. Đội đo INP bằng cách bật trace trong DevTools và dùng thư viện web-vitals thu INP thực địa: INP = 740ms (Poor). Nguyên nhân: khi bấm nút, một loạt JavaScript đồng bộ (validate form, mã hóa, cập nhật state, re-render toàn màn hình) chạy liền mạch, chặn main thread gần 700ms trước khi trình duyệt kịp vẽ lại.

Giải pháp: chia nhỏ tác vụ nặng, hiển thị ngay trạng thái loading (phản hồi thị giác tức thì) rồi mới xử lý phần nặng, và dời phần mã hóa sang Web Worker. INP xuống 150ms. Người dùng thấy phản hồi ngay, hết tình trạng bấm trùng.

Bài học: INP đo cảm giác mượt khi tương tác — thứ hoàn toàn nằm ở phía client. Server nhanh không cứu được một main thread bị JavaScript làm nghẽn.

Hướng dẫn từng bước

Dưới đây là quy trình thực chiến để đưa browser performance vào công việc QA của bạn.

Bước 1 — Chạy Lighthouse lần đầu (chẩn đoán nhanh). Mở Chrome, vào trang cần test, mở DevTools (F12) → tab Lighthouse. Chọn:

  • Mode: Navigation (đo lần tải trang).
  • Device: Mobile (LUÔN test mobile trước — phần lớn traffic Việt Nam là mobile, và mobile khắc nghiệt hơn).
  • Categories: tích Performance.
Bấm "Analyze page load". Đọc điểm tổng và ba Core Web Vitals. Kéo xuống mục "Opportunities" và "Diagnostics" — Lighthouse chỉ thẳng thứ cần sửa (ví dụ "Properly size images", "Reduce unused JavaScript").

Bước 2 — Chạy Lighthouse qua CLI để tự động hóa. Cài và chạy:

npm install -g lighthouse
lighthouse https://vietnamcos.com --preset=desktop --output=json --output=html --output-path=./report
Bản CLI cho phép lưu báo cáo, so sánh theo thời gian, và cắm vào pipeline (chủ đề pipeline sẽ được nối ở các bài CI/CD khác). Dùng --form-factor=mobile --throttling.cpuSlowdownMultiplier=4 để mô phỏng điện thoại yếu.

Bước 3 — Xem field data qua PageSpeed Insights. Truy cập pagespeed.web.dev, nhập URL. Điểm mấu chốt: phần đầu trang hiển thị field data từ CrUX (dữ liệu người dùng thật 28 ngày qua) — đây mới là con số Google dùng đánh giá. Phần dưới là lab data từ Lighthouse để bạn debug. So sánh hai phần: nếu lab tốt mà field xấu, nghĩa là bạn đang test trên máy quá "xịn" so với người dùng thật.

Bước 4 — Điều tra sâu bằng WebPageTest. Vào webpagetest.org. Cấu hình sát người dùng thật của bạn:

  • Test Location: chọn nơi gần khách (ví dụ Singapore cho traffic Đông Nam Á).
  • Browser/Device: chọn một điện thoại Android tầm trung.
  • Connection: chọn "4G" hoặc "3G Fast" để mô phỏng mạng thật.
  • Number of Tests: đặt 3–9 lần rồi lấy giá trị median (trung vị), đừng tin một lần chạy duy nhất.
Sau khi chạy, đọc:
  • Waterfall chart: từng request theo thời gian, thấy ngay file nào chặn, file nào tải muộn.
  • Filmstrip: dải khung hình cho thấy trang "trông như thế nào" ở từng mốc thời gian — cực kỳ hữu ích để giải thích cho người không kỹ thuật.
Bước 5 — Thu thập RUM thực địa với thư viện web-vitals. Lab và WebPageTest chưa đủ. Nhúng đoạn sau để gửi số liệu người dùng thật về hệ thống giám sát của bạn:
import { onLCP, onINP, onCLS } from 'web-vitals';
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
Đây là cách duy nhất thấy được trải nghiệm của toàn bộ người dùng thật, không phải chỉ một máy test.

Bước 6 — Ghi finding và ưu tiên. Với mỗi vấn đề, ghi rõ: chỉ số nào Poor, giá trị đo, thủ phạm cụ thể (file/ảnh/script nào), và tác động kinh doanh ước lượng. Ưu tiên sửa thứ ảnh hưởng field data mobile trước.

Lỗi thường gặp & mẹo

  • Chỉ test Desktop, quên Mobile. Đây là lỗi phổ biến nhất. Điểm desktop luôn đẹp hơn vì CPU mạnh, mạng nhanh. Nhưng khách Việt Nam chủ yếu dùng mobile. Luôn test Mobile với throttling.
  • Tin một lần chạy Lighthouse duy nhất. Lighthouse có độ nhiễu (variance) đáng kể — cùng một trang chạy 5 lần có thể lệch 10–15 điểm do tải máy, script bên thứ ba. Chạy 3–5 lần và lấy median.
  • Nhầm lab data với field data. Báo cáo sếp "Lighthouse 98 điểm rồi, xong" trong khi CrUX vẫn "Poor" là sai lầm chết người. Luôn đối chiếu với field data.
  • Vẫn nhắc FID. FID đã bị INP thay thế từ 2024. Đừng dùng chỉ số lỗi thời trong báo cáo 2026.
  • Đo trang đã cache/đăng nhập sẵn. Test lần đầu (cold, chưa cache, chưa đăng nhập) mới phản ánh trải nghiệm người dùng mới. WebPageTest phân biệt rõ "First View" và "Repeat View" — hãy nhìn cả hai.
  • Bỏ qua script bên thứ ba. Chat widget, pixel quảng cáo, analytics thường là thủ phạm lớn nhất của TBT/INP mà đội dev không để ý vì "nó của bên khác".
  • Mẹo: mục "Treemap" trong Lighthouse cho thấy trực quan JavaScript nào nặng và bao nhiêu phần trăm không được dùng — điểm khởi đầu tuyệt vời để cắt bundle.
  • Mẹo: dùng chế độ so sánh của WebPageTest để đặt cạnh nhau trang của bạn và trang đối thủ — filmstrip song song là "bằng chứng" thuyết phục nhất khi xin ngân sách tối ưu.

Bài tập thực hành

  • Đo cơ bản. Chọn một website Việt Nam bạn hay dùng (ví dụ một trang tin hoặc sàn TMĐT). Chạy Lighthouse ở chế độ Mobile. Ghi lại điểm Performance, LCP, INP (nếu có), CLS. Xác định phần tử LCP là gì (ảnh hero? tiêu đề?).
  • Lab vs Field. Nhập cùng URL đó vào PageSpeed Insights. So sánh Core Web Vitals ở phần field data (CrUX) với phần lab data (Lighthouse). Chúng khác nhau thế nào? Viết 2–3 câu giải thích tại sao.
  • Điều tra waterfall. Chạy cùng URL trên WebPageTest với thiết bị mobile + mạng 4G, 3 lần lấy median. Mở waterfall chart, tìm 3 request nặng nhất hoặc chặn render lâu nhất. Đề xuất một cách cải thiện cho mỗi cái.
  • Truy CLS. Tìm một trang bạn nghi có layout nhảy (thường là trang nhiều quảng cáo). Chạy Lighthouse, xem giá trị CLS và mục "Avoid large layout shifts". Ghi lại phần tử nào gây shift.
  • Nâng cao. Nhúng thư viện web-vitals vào một trang HTML tĩnh đơn giản của riêng bạn, in LCP/INP/CLS ra console. Tương tác với trang (bấm nút) và quan sát INP thay đổi thế nào.

Tóm tắt

  • Backend nhanh không đồng nghĩa người dùng thấy nhanh. Frontend là "last mile" — chặng cuối mà JMeter/k6 không nhìn thấy, và thường là nơi trải nghiệm thật bị phá hỏng.
  • Core Web Vitals gồm ba chỉ số: LCP (tải xong chưa, ≤2.5s), INP (mượt không, ≤200ms), CLS (ổn định không, ≤0.1). INP đã thay FID từ 2024.
  • Lighthouse là công cụ hàng ngày: nhanh, miễn phí, chấm điểm và chỉ ra khuyến nghị. Luôn test Mobile và chạy nhiều lần lấy median.
  • WebPageTest là công cụ điều tra sâu: mô phỏng thiết bị/mạng thật, waterfall và filmstrip vạch mặt từng thủ phạm.
  • Phân biệt lab data và field data. Dùng lab để chẩn đoán, dùng field data (CrUX/RUM) để đánh giá. Đừng bao giờ báo cáo "xong" chỉ dựa trên điểm lab.
  • Là kỹ sư hiệu năng, hãy đo cả hai đầu: server bằng k6/JMeter, và trải nghiệm trình duyệt bằng Lighthouse/WebPageTest. Đó mới là bức tranh hoàn chỉnh về tốc độ mà khách hàng thật cảm nhận.
Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi