Xác định nguyên nhân khiến chỉ số INP kém

Khả năng phản hồi kém đối với các lượt tương tác khiến người dùng có ấn tượng không tốt rằng trang bị chậm hoặc thậm chí là hoàn toàn không hoạt động. Lượt tương tác đến nội dung hiển thị tiếp theo (INP) là một chỉ số dựa trên API Thời gian diễn ra sự kiện. Chỉ số này đo lường lượt tương tác kém nhất (trừ một số điểm ngoại lệ) để đánh giá khả năng phản hồi của trang.

Việc xác định nguyên nhân gốc khiến trang web không phản hồi có thể rất khó khăn, đặc biệt là vì điều này phụ thuộc vào hoạt động tương tác của người dùng và các điều kiện môi trường như chức năng của thiết bị và điều kiện mạng. Điều này khiến việc chẩn đoán trở nên khó khăn hơn so với một tình huống dễ lặp lại và dễ dự đoán hơn như tải trang. Dữ liệu trong phòng thí nghiệm chỉ sao chép một nhóm nhỏ các tình huống của người dùng thực, vì vậy, việc đo lường nguyên nhân gây ra INP chậm trong thực tế là rất cần thiết.

API Thời gian sự kiện cho phép chia thời lượng INP thành 3 phần phụ: Độ trễ đầu vào (quá trình xử lý đã diễn ra khi lượt tương tác xảy ra), Thời lượng xử lý (độ trễ do kết quả trực tiếp của lượt tương tác) và Độ trễ hiển thị (độ trễ do kết xuất khung hình tiếp theo sau lượt tương tác). Việc này giúp xác định xem vấn đề có nằm trong mã khác, mã JavaScript tương tác hay quá trình xử lý của trình duyệt để hiển thị nội dung cập nhật hay không, thay vì chuyển thẳng đến mã tương tác.

Bạn có thể thu thập thêm thông tin chi tiết về JavaScript, tính toán lại kiểu và bố cục làm chậm hoạt động tương tác. Dấu vết hiệu suất đầy đủ bằng cách sử dụng API Tự phân tích tài nguyên JS là một giải pháp nặng nề, có thể gây ra các vấn đề về hiệu suất. Long Animation Frames API là một API gọn nhẹ, có thể dùng trong thực tế để xác định JavaScript chạy chậm và phân biệt giữa chi phí tính toán lại kiểu và chi phí bố cục trong các lượt tương tác INP:

  • Bố cục và kiểu bắt buộc ở cấp tập lệnh (forcedStyleDuration và forcedLayoutDuration): Báo cáo các phép tính lại kiểu đồng bộ (chẳng hạn như gọi getComputedStyle() sau khi đột biến DOM) và bố cục/phân luồng lại bắt buộc (chẳng hạn như đọc getBoundingClientRect() hoặc offsetWidth sau khi đột biến) bên trong một tập lệnh đang chạy trong Độ trễ đầu vào hoặc Thời lượng xử lý.
  • Kiểu và bố cục kết xuất ở cấp khung hình (styleDuration và layoutDuration): Báo cáo thời gian dành cho việc tính toán lại kiểu so với việc cập nhật bố cục trong quá trình cập nhật kết xuất của khung hình, giúp giải thích Độ trễ hiển thị cao.

Việc phân biệt giữa tính toán lại kiểu và bố cục là rất quan trọng vì chúng có nguyên nhân gốc và cách khắc phục khác nhau: thời lượng kiểu cao cho thấy việc so khớp bộ chọn CSS tốn kém hoặc vô hiệu hoá kiểu rộng (được khắc phục bằng cách đơn giản hoá bộ chọn hoặc giảm phạm vi quy tắc), trong khi thời lượng bố cục cao cho thấy việc tính toán hình học tốn kém hoặc bố cục đồng bộ bị gián đoạn (được khắc phục bằng cách đọc DOM theo lô trước khi ghi, đơn giản hoá cấu trúc DOM hoặc sử dụng vùng chứa CSS).

Cách triển khai

Việc tính toán INP từ Event Timing API thô rất phức tạp và có một số điểm khác biệt. Bạn nên sử dụng một công cụ hoặc thư viện RUM để thu thập dữ liệu này.

web-vitals là một thư viện nguồn mở của Google, giúp tính toán các chỉ số Core Web Vitals (bao gồm cả INP), đồng thời bao gồm các phần phụ và dữ liệu khung hoạt hoạ cần nhiều thời gian cho lượt tương tác INP.

Nhận thông tin phân bổ cho các lượt tương tác INP bằng thư viện web-vitals

Thư viện web-vitals là một thư viện nhỏ dùng để đo lường Core Web Vitals và các chỉ số hiệu suất khác. Bạn có thể dùng hàm onINP() để xác định lượt tương tác chậm nhất và hàm này bao gồm thông tin về các phần phụ của INP, tập lệnh được thực thi trong lượt tương tác, cũng như thời lượng của kiểu và bố cục bằng cách sử dụng API Khung hoạt hoạ cần nhiều thời gian.

// Use the attribution build to get Long Animation Frame data
// alongside the INP metric value.
import { onINP } from 'web-vitals/attribution';

onINP((metric) => {
  const { attribution } = metric;
  const longestScript = attribution.longestScript;

  // Sum rendering-phase style and layout durations across all LoAF entries
  // intersecting the INP interaction (primarily explains presentationDelay).
  let renderStyleDuration = 0;
  let renderLayoutDuration = 0;
  for (const entry of attribution.longAnimationFrameEntries) {
    renderStyleDuration += entry.styleDuration ?? 0;
    renderLayoutDuration += entry.layoutDuration ?? 0;
  }

  // Beacon script, style, and layout attribution for the INP interaction,
  // so you can identify the root cause in production.
  navigator.sendBeacon(
    '/analytics',
    JSON.stringify({
      name: 'INP',
      value: metric.value,
      // These fields give the INP subparts:
      inputDelay: attribution.inputDelay,
      presentationDelay: attribution.presentationDelay,
      processingDuration: attribution.processingDuration,
      interactionTarget: attribution.interactionTarget,
      // These fields identify which script function was responsible
      // for the longest processing during the INP interaction.
      invokerType: longestScript?.entry?.invokerType,
      sourceURL: longestScript?.entry?.sourceURL,
      sourceFunctionName: longestScript?.entry?.sourceFunctionName,
      sourceCharPosition: longestScript?.entry?.sourceCharPosition,
      // subpart indicates which phase (input delay, processing, or
      // presentation delay) the longest script overlapped with most.
      subpart: longestScript?.subpart,
      intersectingDuration: longestScript?.intersectingDuration,
      // Synchronous style and layout work forced inside the longest script
      // (with forcedStyleAndLayoutDuration as a combined fallback):
      forcedStyleDuration: longestScript?.entry?.forcedStyleDuration,
      forcedLayoutDuration: longestScript?.entry?.forcedLayoutDuration,
      forcedStyleAndLayoutDuration: longestScript?.entry?.forcedStyleAndLayoutDuration,
      // Rendering-phase style and layout work across the interaction's frames
      // (with totalStyleAndLayoutDuration as a combined fallback):
      renderStyleDuration,
      renderLayoutDuration,
      totalStyleAndLayoutDuration: attribution.totalStyleAndLayoutDuration
    })
  );
});

Các phương pháp hay nhất

  • NÊN sử dụng các phần phụ của INP ban đầu để xác định xem độ trễ có phải là do JavaScript đang chạy (độ trễ đầu vào), trình xử lý sự kiện JavaScript cho hoạt động tương tác (thời gian xử lý) hay hoạt động kết xuất tiếp theo (độ trễ hiển thị) hay không.
  • NÊN cố gắng xác định JavaScript chặn lớn nhất dưới dạng thông tin chi tiết bổ sung, đặc biệt là đối với độ trễ đầu vào và thời gian xử lý.
  • NÊN phân biệt giữa việc tính toán lại kiểu (styleDuration, forcedStyleDuration) và bố cục (layoutDuration, forcedLayoutDuration) khi chẩn đoán thời gian xử lý cao hoặc độ trễ hiển thị: thời gian kiểu cao cho biết bộ chọn CSS tốn kém hoặc vô hiệu hoá rộng, trong khi thời gian bố cục cao cho biết điểm tắc nghẽn hình học DOM hoặc bố cục đồng bộ bị hỏng.
  • NÊN ưu tiên Long Animation Frames API để cung cấp thêm thông tin chi tiết này thay vì các giải pháp thay thế như JS Self-Profiling API, vốn có mức hao tổn thời gian chạy cao hơn.
  • NÊN sử dụng thư viện web-vitals nếu không có giải pháp RUM nào khác. Công cụ này có thể xác định lượt tương tác INP và cung cấp thông tin về các phần phụ cũng như tập lệnh được thực thi trong lượt tương tác (bằng cách sử dụng API Khung hoạt hoạ cần nhiều thời gian).
  • NÊN gửi lại thông tin bắt buộc đến một dịch vụ phân tích thay vì chỉ ghi lại thông tin đó trên thiết bị.

Hỗ trợ trình duyệt và chiến lược dự phòng

Event Timing API có trong hầu hết các trình duyệt hiện đại và cần thiết để tính toán INP cũng như các phần phụ của INP. Đối với những trình duyệt không hỗ trợ API này, bạn không thể đo lường INP.

API Khung ảnh động dài cung cấp thêm thông tin chi tiết (không bắt buộc). Bạn có thể sử dụng một cách an toàn mà không cần dự phòng. Trong hầu hết các trường hợp, những cơ hội cải thiện hiệu suất mà công cụ này xác định cũng sẽ áp dụng cho các trình duyệt khác.

Nếu mục tiêu Baseline của bạn hỗ trợ Long Animation Frames API (long-animation-frames) nhưng không hỗ trợ các thuộc tính thời lượng kiểu và bố cục riêng biệt (styleDuration và layoutDuration trên PerformanceLongAnimationFrameTiming hoặc forcedStyleDuration và forcedLayoutDuration trên PerformanceScriptTiming), hãy quay lại thời gian kiểu và bố cục kết hợp:

  • Sử dụng script.forcedStyleAndLayoutDuration trên PerformanceScriptTiming cho kiểu bắt buộc đồng bộ và bố cục hoạt động bên trong tập lệnh.
  • Sử dụng metric.attribution.totalStyleAndLayoutDuration trong web-vitals/attribution (hoặc tính toán (entry.paintTime || entry.startTime + entry.duration) - entry.styleAndLayoutStart trên mỗi mục nhập PerformanceLongAnimationFrameTiming) để kết hợp kiểu và bố cục.