Mọi kỹ thuật tối ưu hóa đằng sau một điểm Lighthouse hoàn hảo

Tôi chạy Lighthouse trên trang web của mình liên tục. Nó luôn đạt 100 trong mọi lần chạy local và 100 trong Real Experience Score của Vercel.

Những điểm số này không đến từ một danh sách kiểm tra chung chung. Tôi đạt được chúng bằng cách chuyển bớt công việc từ trình duyệt sang giai đoạn build.

Đây chính xác là cách tôi thực hiện.

Chuyển công việc sang thời điểm build

Hầu hết các hướng dẫn đều bảo bạn hãy lazy-load mọi thứ. Tôi lại ưu tiên prerender. Tôi sử dụng TanStack Start với tính năng prerendering đã được bật.

Điều này biến toàn bộ trang web thành HTML tĩnh trong quá trình build. Trình duyệt không cần phải thực thi các đoạn JavaScript nặng nề chỉ để hiển thị trang đầu tiên. HTML đã có sẵn ngay khi người dùng nhấn Enter.

Tính toán trước các logic phức tạp

Trang chủ của tôi có một bản đồ thế giới với 5.000 dấu chấm. Thông thường, một thư viện sẽ phân tích GeoJSON và thực hiện các phép toán trên main thread. Điều này làm chặn trang trong 1.000ms.

Tôi đã giải quyết vấn đề này bằng cách chuyển các phép toán sang một build script.

  • Tôi tạo ra một chuỗi SVG path duy nhất cho tất cả 5.000 dấu chấm.
  • Một path duy nhất sẽ giúp trình duyệt render nhanh hơn nhiều so với 5.000 hình tròn riêng biệt.
  • Tôi tính toán trước các bảng tra cứu tọa độ (coordinate lookup tables) để trình duyệt không phải thực hiện bất kỳ phép toán nào khi runtime.

Độ trễ 1.000ms trở thành một lần paint duy nhất và tức thì.

Tối ưu hóa việc tải font

Tôi sử dụng rel="preload" cho các font chính của mình.

Một sai lầm phổ biến là quên thuộc tính crossOrigin. Nếu bạn bỏ qua nó, trình duyệt sẽ tải font hai lần. Điều này làm hỏng chỉ số Largest Contentful Paint (LCP) của bạn. Tôi chỉ preload ba font được sử dụng ở phần "above the fold" (phần hiển thị đầu tiên trên màn hình).

Sử dụng đúng công cụ cho animation

Tôi sử dụng SMIL cho các hiệu ứng pulse (nhịp đập) đơn giản trên các marker bản đồ. Nó "rẻ" hơn so với việc sử dụng React state để điều khiển một vòng lặp animation. Nó cho phép trình duyệt xử lý công việc trên compositor thread.

Đối với các path phức tạp, tôi sử dụng motion. Tôi giữ mọi thứ đơn giản. Tôi chỉ animate một lần khi mount và tránh việc lắng nghe vị trí cuộn (scroll positions).

Ưu tiên vector và WebP

Nếu là logo hoặc hình khối, hãy dùng SVG. Nếu là ảnh, hãy dùng WebP. Điều này giúp giữ dung lượng file thấp và ngăn chặn hiện tượng layout shifts.

Tránh việc over-engineering

Tôi không dùng image CDN. Tôi cũng không dùng code-splitting phức tạp. Trang web của tôi nhỏ, nên chia tách ở cấp độ route (route-level splitting) là đủ.

Một điểm số hoàn hảo có thể chỉ là một chỉ số phù phiếm (vanity metric). Mục tiêu thực sự là đo lường hiệu suất và chuyển càng nhiều công việc càng tốt ra khỏi thiết bị của người dùng.

My Portfolio: brodin.dev

Source code: github.com/NathanBrodin/Portfolio

TanStack Start prerendering: tanstack.com/start

Paper Shaders: shaders.paper.design

Full post: https://dev.to/nathan-brodin/every-optimization-behind-a-perfect-lighthouse-score-283n