Core Web Vitals 2026: Tối ưu INP, LCP, CLS để tăng SEO và conversion rate

một tháng trước · 8 phút đọc
Tại sao Core Web Vitals quan trọng hơn bao giờ hết
Mình nhớ hồi 2021, khi Google thông báo Core Web Vitals sẽ là ranking signal - cộng đồng dev phản ứng kiểu "ừ thì biết rồi, tối ưu sau". Rồi sau là mãi mãi không làm.
Đến 2024, Google chính thức thay thế FID bằng INP (Interaction to Next Paint). Đến 2026, ba chỉ số LCP, INP, CLS không chỉ ảnh hưởng đến ranking - chúng ảnh hưởng trực tiếp đến conversion rate. 32% increase in bounce probability when mobile page load time increases from 1 second to 3 seconds (Google Think with Google Mobile Speed Study) cho thấy mỗi giây trễ thêm trong load time tương quan với tỷ lệ bounce tăng đáng kể.
Nếu bạn đang build web app với React, Vue, hay bất kỳ framework nào - bài này là checklist thực chiến bạn cần trước khi deploy production.
Ba chỉ số - ba vấn đề khác nhau, cần cách xử lý khác nhau
Mục tiêu cụ thể Google đang dùng để phân loại:
- LCP (Largest Contentful Paint): ≤ 2.5s là tốt, > 4s là kém
- INP (Interaction to Next Paint): ≤ 200ms là tốt, > 500ms là kém
- CLS (Cumulative Layout Shift): ≤ 0.1 là tốt, > 0.25 là kém
Đo lường trước, tối ưu sau
Tối ưu mà không có số liệu thì cũng như đi trong tối. Bước đầu tiên luôn là đo.
PageSpeed Insights
Truy cập PageSpeed Insights, nhập URL và bạn sẽ thấy điểm Core Web Vitals từ cả lab data (Lighthouse) lẫn field data (real users). Hai loại này khác nhau - lab data chạy trong môi trường kiểm soát, field data là số liệu thực từ người dùng Chrome.
Field data quan trọng hơn vì Google dùng nó để rank.
Web Vitals library trong code
Thêm thư viện web-vitals vào project để đo real user metrics (RUM) và gửi về analytics:
Field data từ real users quan trọng hơn lab score - đừng chỉ nhìn Lighthouse
Chrome DevTools
Mở DevTools → tab Performance → Record khi tương tác. Bạn sẽ thấy chính xác đoạn JavaScript nào block main thread lâu nhất. Tìm những đoạn màu đỏ trong flame chart - đó là long tasks cần xử lý.
Search Console
Google Search Console có tab Core Web Vitals riêng, phân loại URL theo nhóm "Kém/Cần cải thiện/Tốt". Đây là nguồn dữ liệu field data miễn phí, trực tiếp từ Google - check hàng tuần là hợp lý.
Tối ưu LCP - Largest Contentful Paint
LCP đo thời gian để phần tử lớn nhất (thường là hero image hoặc heading lớn) xuất hiện trong viewport. Kém LCP thường do 3 nguyên nhân chính: server response chậm, resource load chậm, hoặc render-blocking resources.
Tối ưu hình ảnh hero
Đây là nguyên nhân số 1. Ảnh hero thường là phần tử LCP - load chậm là điểm LCP tệ ngay.
Chuyển sang định dạng WebP hoặc AVIF - AVIF nhỏ hơn JPEG khoảng AVIF is typically 45-50% smaller than JPEG at comparable quality for web images (Web.dev and independent web image benchmarks (2023-2024)) trong khi chất lượng tương đương. Nếu dùng Next.js, component <Image> làm điều này tự động.
Tối ưu server response (TTFB)
LCP không thể tốt nếu server response (TTFB) chậm. Một số cách nhanh:
- Dùng CDN để serve static assets gần người dùng hơn
- Enable HTTP/2 hoặc HTTP/3 trên server
- Implement server-side caching (Redis) cho data thường xuyên truy cập
Font loading
Preload ảnh hero + WebP/AVIF là combo đơn giản nhất để cải thiện LCP nhanh
Tối ưu INP - Interaction to Next Paint
INP là chỉ số mới nhất và cũng khó tối ưu nhất. Nó đo thời gian từ khi người dùng tương tác (click, keypress, tap) đến khi browser render frame tiếp theo. Khác với FID chỉ đo lần tương tác đầu tiên, INP đo tất cả các tương tác trong suốt session.
Vấn đề ở đâu? Long tasks trên main thread.
Break up long tasks
Bất kỳ task nào chạy quá 50ms đều block main thread và làm INP tệ. Cách fix là chia nhỏ:
Tối ưu event handlers
Yield control về cho browser sau mỗi chunk - người dùng không bao giờ cảm thấy bị block
Code splitting với dynamic import
Đừng load toàn bộ JavaScript ngay từ đầu. Dùng dynamic import để chỉ load code khi cần:
Nếu bạn muốn đi sâu hơn vào JavaScript async patterns và event loop - khóa Lập Trình JavaScript Nâng Cao có phần cover closure, microtask queue khá chi tiết, giúp hiểu tại sao setTimeout(r, 0) lại hoạt động như vậy.
Tối ưu CLS - Cumulative Layout Shift
CLS đo tổng lượng layout shift không mong đợi. Bạn đã từng vào web, chuẩn bị click một nút thì nó nhảy xuống vì ảnh load xong? Đó là CLS.
CLS thường bị gây ra bởi: ảnh không có kích thước, ads/embeds chèn vào sau khi render, và font swap.
Luôn đặt width và height cho ảnh
Skeleton loading thay vì không có gì
Skeleton giữ đúng kích thước trước khi content load - CLS = 0
Tránh layout shift do font
font-display: swap giải quyết FOIT nhưng lại tạo ra layout shift nhỏ khi font swap xảy ra. Giải pháp tốt hơn là dùng font-display: optional với font preload:
Lần đầu người dùng truy cập sẽ thấy fallback font, từ lần hai trở đi font custom được dùng ngay. Trade-off: lần đầu tiên không thấy font custom - chấp nhận được với hầu hết use case.
Tải Tải CLS checklist đầy đủ để có checklist đầy đủ tất cả các nguyên nhân gây CLS phổ biến.
Caching, CDN và lazy loading - bộ ba không thể thiếu
Ba kỹ thuật này không giải quyết một chỉ số cụ thể mà cải thiện toàn bộ performance tổng thể.
Lazy loading images
Browser hỗ trợ native lazy loading đã 95%+ browser support for native lazy loading (loading=lazy) on img elements in 2024 (Can I Use / Web Platform Data). Không cần thư viện ngoài cho ảnh thông thường.
Service Worker cho caching
Stale-while-revalidate: người dùng thấy content ngay từ cache, browser tự update ngầm
CDN setup cơ bản
Nếu chưa dùng CDN, đây là điểm tối ưu có ROI cao nhất. Cloudflare free tier là đủ cho hầu hết project:
- Trỏ nameserver domain về Cloudflare
- Bật "Auto Minify" cho JS/CSS/HTML
- Bật "Brotli compression"
- Set Cache-Control headers phù hợp từ origin server
Static assets (images, fonts, JS bundles) nên cache ít nhất 1 năm với content-hash trong filename - khi file thay đổi, hash thay đổi, URL mới, cache tự invalidate.