T
Trinh Digital
Triển khai Giải pháp

PageSpeed 100 điểm: Hướng dẫn tối ưu từng chỉ số Core Web Vitals

Trinh Digital · · 10 phút đọc

Tối ưu PageSpeed không phải chạy theo điểm số — mà là hệ thống hóa các kỹ thuật để website load nhanh, tương tác mượt, và ổn định trực quan. Bài viết này hướng dẫn chi tiết cách cải thiện từng chỉ số Core Web Vitals: LCP, INP, và CLS — với checklist cụ thể, tool miễn phí, và ví dụ code thực tế.

🎁 Lead magnet: Tải miễn phí Performance Optimization Checklist — 30 điểm kiểm tra hiệu suất website, chia theo priority, có hướng dẫn fix cho từng điểm.

Core Web Vitals là gì?

3 chỉ số chính

Chỉ sốĐo gìTốtCần cải thiệnKém
LCP (Largest Contentful Paint)Thời gian render phần tử lớn nhất (hero image, heading)≤2.5s2.5-4s>4s
INP (Interaction to Next Paint)Độ responsive khi user tương tác (click, tap, type)≤200ms200-500ms>500ms
CLS (Cumulative Layout Shift)Layout có bị “nhảy” khi load không≤0.10.1-0.25>0.25

Tại sao 3 chỉ số này quan trọng?

  • LCP: User đánh giá “website nhanh hay chậm” dựa trên lúc nhìn thấy nội dung chính
  • INP: Khi click button mà 500ms sau mới phản hồi → user nghĩ website hỏng
  • CLS: Đang đọc text → layout nhảy → bấm nhầm nút → user tức giận

Google dùng cả 3 làm ranking factor cho SEO.

Tối ưu LCP (Largest Contentful Paint)

LCP phần tử nào?

LCP thường là 1 trong:

  • Hero image/banner
  • Heading text lớn (<h1>)
  • Video poster image
  • Background image lớn

Cách xác định: Chrome DevTools → Performance tab → xem “LCP” marker, hoặc PageSpeed Insights hiện rõ phần tử LCP.

Nguyên nhân LCP chậm và cách fix

Nguyên nhân 1: Server response chậm (TTFB cao)

Vấn đề: Server mất 2-3 giây để trả response đầu tiên.

Chẩn đoán: PageSpeed Insights → “Reduce server response times (TTFB)”

Giải pháp:

ActionCải thiện TTFBEffort
CDN (Cloudflare Free)-50-70%15 phút setup
Upgrade hosting (shared → VPS)-40-60%1-2 giờ
Server-side caching (Redis, Varnish)-60-80%2-4 giờ
Database optimization-20-40%4-8 giờ
Static site generation (SSG)-90%1-2 tuần (rebuild)

Nguyên nhân 2: Hình ảnh LCP nặng

Vấn đề: Hero image 2MB JPEG, load mất 3 giây.

Giải pháp:

<!-- Trước: JPEG 2MB, không responsive -->
<img src="hero.jpg" alt="Hero banner">

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

<picture>
  <source srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
          type="image/webp" sizes="100vw">
  <img src="hero-800.jpg" alt="Hero banner"
       width="1200" height="600" fetchpriority="high">
</picture>

Checklist hình ảnh LCP:

  • Format: WebP hoặc AVIF
  • Size: ≤200KB (cho hero image)
  • Responsive: srcset với nhiều kích thước
  • Preload: <link rel="preload"> cho LCP image
  • fetchpriority="high" trên LCP element
  • KHÔNG lazy load LCP image

Nguyên nhân 3: Render-blocking resources

Vấn đề: CSS/JS nặng block rendering — browser phải download xong mới render.

Giải pháp:

<!-- Trước: CSS block rendering -->
<link rel="stylesheet" href="styles.css"> <!-- 200KB -->

<!-- Sau: Inline critical CSS + async load full CSS -->
<style>
  /* Critical CSS — chỉ CSS cần cho above-the-fold */
  body { font-family: sans-serif; }
  .hero { background: #f5f5f5; height: 60vh; }
  /* ... ~5KB */
</style>
<link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'">
<!-- JS: defer thay vì block -->
<script src="app.js" defer></script>
<!-- hoặc -->
<script src="analytics.js" async></script>

Nguyên nhân 4: Web fonts chậm

Vấn đề: Custom font 200KB, download từ Google Fonts → text invisible trong 2 giây.

Giải pháp:

<!-- Preload font quan trọng nhất -->
<link rel="preload" href="/fonts/Inter-Regular.woff2" as="font"
      type="font/woff2" crossorigin>

<!-- Font display swap: hiện text ngay bằng system font, swap khi custom font ready -->
<style>
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Regular.woff2') format('woff2');
  font-display: swap;
}
</style>

Tips thêm: Subset font (chỉ giữ ký tự cần dùng), self-host thay vì Google Fonts (giảm 1 DNS lookup).

LCP Optimization Checklist

#ActionImpactEffort
1Preload LCP imageCao5 phút
2Convert LCP image sang WebPCao15 phút
3Inline critical CSSCao1-2 giờ
4CDN (Cloudflare)Cao15 phút
5Defer non-critical JSTrung bình30 phút
6Preload web fontTrung bình10 phút
7Server cachingCao2-4 giờ
8Reduce TTFB (upgrade hosting)Cao1-2 giờ

Tối ưu INP (Interaction to Next Paint)

INP đo gì?

INP đo thời gian từ khi user tương tác (click, tap, keypress) đến khi browser cập nhật visual. Bao gồm:

  1. Input delay: Event handler chờ main thread rảnh
  2. Processing time: Event handler chạy
  3. Presentation delay: Browser render kết quả

Nguyên nhân INP kém

Nguyên nhânVí dụImpact
Long tasks trên main threadJS xử lý nặng >50msCao
Third-party scriptsChat widget, analytics, adsCao
DOM size quá lớn>1,500 elementsTrung bình
Layout thrashingJS đọc/ghi DOM liên tụcTrung bình

Giải pháp INP

1. Break long tasks:

// Trước: 1 long task 200ms
function processData(items) {
  items.forEach(item => heavyComputation(item)); // block 200ms
}

// Sau: yield to main thread
async function processData(items) {
  for (const item of items) {
    heavyComputation(item);
    // Yield mỗi 50ms để browser render
    if (performance.now() - startTime > 50) {
      await new Promise(resolve => setTimeout(resolve, 0));
      startTime = performance.now();
    }
  }
}

2. Defer third-party scripts:

<!-- Load chat widget sau khi page load xong -->
<script>
window.addEventListener('load', () => {
  setTimeout(() => {
    // Load Tawk.to, Intercom, etc.
    const script = document.createElement('script');
    script.src = 'https://embed.tawk.to/...';
    document.head.appendChild(script);
  }, 3000); // Delay 3 giây sau page load
});
</script>

3. Debounce input handlers:

// Search input: không gọi API mỗi keystroke
const debouncedSearch = debounce((query) => {
  fetch(`/api/search?q=${query}`);
}, 300); // Đợi 300ms sau keystroke cuối

input.addEventListener('input', (e) => debouncedSearch(e.target.value));

Tối ưu CLS (Cumulative Layout Shift)

CLS xảy ra khi nào?

Layout shift = phần tử trên trang di chuyển vị trí sau khi đã render. User thấy trang “nhảy.”

Nguyên nhân CLS phổ biến

#Nguyên nhânVí dụ
1Image/video không có dimensionsẢnh load → đẩy text xuống
2Ads/banner inject vào DOMQuảng cáo xuất hiện → đẩy content
3Web font swapText thay đổi kích thước khi font load
4Dynamic content insert”Bạn có thể thích…” load sau → đẩy content
5Cookie consent bannerBanner xuất hiện → đẩy content xuống

Giải pháp CLS

1. Luôn set width/height cho img/video:

<!-- Trước: CLS khi ảnh load -->
<img src="product.webp" alt="Product">

<!-- Sau: Browser dành sẵn không gian -->
<img src="product.webp" alt="Product" width="400" height="300">
<!-- hoặc dùng aspect-ratio CSS -->
<style>
  img { aspect-ratio: 4/3; width: 100%; height: auto; }
</style>

2. Dành sẵn không gian cho ads/banners:

.ad-slot {
  min-height: 250px; /* Dành sẵn không gian cho ad */
  background: #f5f5f5; /* Placeholder color */
}

3. Font display: optional (thay vì swap):

@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter.woff2') format('woff2');
  font-display: optional; /* Dùng system font nếu custom font chưa sẵn */
}

4. Transform thay vì thay đổi layout properties:

/* Trước: gây layout shift */
.element { top: 10px; left: 20px; }

/* Sau: chỉ composite, không layout shift */
.element { transform: translate(20px, 10px); }

Bảng tổng hợp: 20 kỹ thuật tối ưu PageSpeed

#Kỹ thuậtChỉ số cải thiệnImpactEffort
1CDN (Cloudflare)LCP, TTFBCaoThấp
2Image optimization (WebP/AVIF)LCPCaoThấp
3Lazy loading (images)LCP (cho below-fold)Trung bìnhThấp
4Critical CSS inlineLCPCaoTrung bình
5Preload LCP resourceLCPCaoThấp
6Defer JSLCP, INPCaoThấp
7GZIP/Brotli compressionLCPCaoThấp
8Browser cachingLCP (repeat visits)Trung bìnhThấp
9Reduce DOM sizeINPTrung bìnhTrung bình
10Break long tasksINPCaoTrung bình
11Debounce handlersINPTrung bìnhThấp
12Set image dimensionsCLSCaoThấp
13Font display: swap/optionalCLS, LCPTrung bìnhThấp
14Placeholder for dynamic contentCLSCaoThấp
15Code splittingINP, LCPCaoCao
16Tree shakingLCPTrung bìnhTrung bình
17Server-side rendering (SSR)LCPCaoCao
18Database index optimizationLCP (TTFB)CaoTrung bình
19HTTP/2 or HTTP/3LCPTrung bìnhThấp
20Remove unused CSS/JSLCP, INPTrung bìnhTrung bình

Tool hỗ trợ

ToolMục đíchFree
PageSpeed InsightsMeasure Core Web Vitals
Lighthouse (Chrome)Detailed audit
WebPageTestMulti-location, filmstrip
CrUX DashboardReal user data
SquooshImage optimization
PurgeCSSRemove unused CSS
Bundle AnalyzerVisualize JS bundle
Chrome DevToolsDebug performance

FAQ — Câu hỏi thường gặp

1. Bao lâu thì Google update Core Web Vitals data?

CrUX data (Chrome User Experience Report) update mỗi 28 ngày. Sau khi optimize, đợi ~1 tháng để Google nhận diện cải thiện. PageSpeed Insights hiện cả lab data (ngay lập tức) và field data (28 ngày).

2. Trang nào cần tối ưu trước?

Ưu tiên: (1) Trang chủ, (2) Landing pages chạy ads, (3) Trang sản phẩm/dịch vụ, (4) Blog posts có traffic cao. Dùng Google Search Console → Core Web Vitals report để xem trang nào “kém.”

3. WordPress page builder (Elementor, Divi) có tối ưu được không?

Khó đạt 90+ mobile với page builder vì: DOM quá lớn, CSS/JS nặng, inline styles. Tối ưu được lên 60-75. Nếu cần 90+, cân nhắc custom theme hoặc framework nhẹ (Astro, Next.js, hoặc GeneratePress + custom blocks).

Kết luận

Tối ưu PageSpeed là cuộc chơi của chi tiết — mỗi kỹ thuật nhỏ cải thiện 100-500ms, cộng dồn lại tạo ra trải nghiệm nhanh vượt trội. Bắt đầu với quick wins (CDN, image optimization, defer JS) → đã cải thiện 40-60%. Sau đó deep dive vào từng chỉ số Core Web Vitals.

Nếu bạn cần hỗ trợ audit và tối ưu hiệu suất website, hãy liên hệ Trinh Digital — chúng tôi cung cấp dịch vụ Performance Optimization chuyên nghiệp cho website mọi nền tảng.

#LCP#PageSpeed#Core Web Vitals#CLS
Chia sẻ: Z

Sẵn sàng chuyển đổi số cùng Trinh Digital?

Liên hệ ngay để nhận tư vấn miễn phí. Đội ngũ chuyên gia sẽ phân tích nhu cầu và đề xuất giải pháp tối ưu.

Zalo