Tìm các hoạt động tương tác chậm trong trường

Tìm hiểu cách tìm các hoạt động tương tác diễn ra chậm trong dữ liệu thực tế của trang web để bạn có thể tìm ra cơ hội cải thiện chỉ số Hoạt động tương tác với thời gian hiển thị tiếp theo.

Dữ liệu thực địa là dữ liệu cho biết trải nghiệm của người dùng thực tế trên trang web của bạn. Công cụ này sẽ chỉ ra những vấn đề mà bạn không thể tìm thấy chỉ trong dữ liệu phòng thí nghiệm. Đối với Lượt tương tác đến nội dung hiển thị tiếp theo (INP), dữ liệu thực tế là yếu tố cần thiết để xác định các lượt tương tác diễn ra chậm, đồng thời cung cấp những thông tin quan trọng giúp bạn khắc phục các lượt tương tác đó.

Trong hướng dẫn này, bạn sẽ tìm hiểu cách nhanh chóng đánh giá INP của trang web bằng dữ liệu thực tế từ Báo cáo trải nghiệm người dùng trên Chrome (CrUX) để xem trang web của bạn có gặp vấn đề về INP hay không. Sau đó, bạn sẽ tìm hiểu cách sử dụng bản dựng phân bổ của thư viện web-vitals JavaScript và thông tin chi tiết mới mà thư viện này cung cấp từ Long Animation Frames API (LoAF) để thu thập và diễn giải dữ liệu thực tế cho các hoạt động tương tác diễn ra chậm trên trang web của bạn.

Bắt đầu với CrUX để đánh giá INP của trang web

Nếu bạn không thu thập dữ liệu thực tế tại trang của người dùng trên trang web, thì CrUX có thể là một điểm khởi đầu tốt. CrUX thu thập dữ liệu thực tế tại trang từ những người dùng Chrome thực tế đã chọn gửi dữ liệu đo từ xa.

Dữ liệu CrUX xuất hiện ở một số khu vực khác nhau, tuỳ thuộc vào phạm vi thông tin mà bạn đang tìm kiếm. CrUX có thể cung cấp dữ liệu về INP và các Chỉ số quan trọng chính khác của trang web cho:

  • Từng trang và toàn bộ nguồn gốc bằng PageSpeed Insights.
  • Các loại trang. Ví dụ: nhiều trang web thương mại điện tử có các loại Trang chi tiết sản phẩm và Trang danh sách sản phẩm. Bạn có thể nhận dữ liệu CrUX cho các loại trang riêng biệt trong Search Console.

Để bắt đầu, bạn có thể nhập URL trang web của mình vào PageSpeed Insights. Sau khi bạn nhập URL, dữ liệu thực tế cho URL đó (nếu có) sẽ xuất hiện cho nhiều chỉ số, bao gồm cả INP. Bạn cũng có thể sử dụng các nút bật/tắt để kiểm tra giá trị INP cho phương diện thiết bị di động và máy tính.

Dữ liệu thực tế như CrUX cho thấy trong PageSpeed Insights, cho thấy LCP, INP, CLS tại 3 Chỉ số quan trọng chính của trang web, TTFB, FCP dưới dạng các chỉ số chẩn đoán và FID dưới dạng chỉ số quan trọng chính đã ngừng hoạt động của trang web.
Thông tin về dữ liệu CrUX như trong PageSpeed Insights. Trong ví dụ này, INP của trang web đã cho cần được cải thiện.

Dữ liệu này rất hữu ích vì cho bạn biết liệu bạn có gặp vấn đề hay không. Tuy nhiên, CrUX không thể cho bạn biết điều gì đang gây ra vấn đề. Có nhiều giải pháp Giám sát người dùng thực (RUM) có thể giúp bạn thu thập dữ liệu thực tế của riêng mình từ người dùng trang web để trả lời câu hỏi đó. Một lựa chọn là tự thu thập dữ liệu thực tế bằng thư viện web-vitals JavaScript.

Thu thập dữ liệu thực địa bằng thư viện JavaScript web-vitals

Thư viện JavaScript web-vitals là một tập lệnh mà bạn có thể tải trên trang web của mình để thu thập dữ liệu trường từ người dùng trang web. Bạn có thể sử dụng công cụ này để ghi lại một số chỉ số, bao gồm cả INP trong những trình duyệt hỗ trợ chỉ số này.

Browser Support

  • Chrome: 96.
  • Edge: 96.
  • Firefox Technology Preview: supported.
  • Safari: not supported.

Source

Bạn có thể dùng bản dựng tiêu chuẩn của thư viện web-vitals để lấy dữ liệu INP cơ bản từ người dùng tại hiện trường:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  console.log(name);    // 'INP'
  console.log(value);   // 512
  console.log(rating);  // 'poor'
});

Để phân tích dữ liệu trường của người dùng, bạn cần gửi dữ liệu này đến một nơi nào đó:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  // Prepare JSON to be sent for collection. Note that
  // you can add anything else you'd want to collect here:
  const body = JSON.stringify({name, value, rating});

  // Use `sendBeacon` to send data to an analytics endpoint.
  // For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
  navigator.sendBeacon('/analytics', body);
});

Tuy nhiên, dữ liệu này không cho bạn biết nhiều hơn so với CrUX. Đó là lúc bạn cần dùng bản dựng phân bổ của thư viện web-vitals.

Phát triển hơn nữa với bản dựng phân bổ của thư viện web-vitals

Bản dựng phân bổ của thư viện web-vitals cung cấp thêm dữ liệu mà bạn có thể nhận được từ người dùng thực tế để giúp bạn khắc phục hiệu quả hơn những tương tác có vấn đề đang ảnh hưởng đến chỉ số INP của trang web. Bạn có thể truy cập vào dữ liệu này thông qua đối tượng attribution xuất hiện trong phương thức onINP() của thư viện:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, rating, attribution}) => {
  console.log(name);         // 'INP'
  console.log(value);        // 56
  console.log(rating);       // 'good'
  console.log(attribution);  // Attribution data object
});
Cách nhật ký bảng điều khiển xuất hiện trong thư viện web-vitals. Bảng điều khiển trong ví dụ này cho thấy tên của chỉ số (INP), giá trị INP (56), vị trí của giá trị đó trong các ngưỡng INP (tốt) và nhiều thông tin được hiển thị trong đối tượng phân bổ, bao gồm cả các mục từ Long Animation Frames API.
Cách dữ liệu từ thư viện web-vitals xuất hiện trong bảng điều khiển.

Ngoài chính INP của trang, bản dựng phân bổ còn cung cấp nhiều dữ liệu mà bạn có thể dùng để hiểu rõ lý do khiến các lượt tương tác diễn ra chậm, bao gồm cả phần tương tác mà bạn nên tập trung vào. Báo cáo này có thể giúp bạn trả lời những câu hỏi quan trọng như:

  • "Người dùng có tương tác với trang trong khi trang đang tải không?"
  • "Trình xử lý sự kiện của hoạt động tương tác có chạy trong thời gian dài không?"
  • "Mã trình xử lý sự kiện tương tác có bị trì hoãn khi bắt đầu không? Nếu có, thì còn có những hoạt động nào khác diễn ra trên luồng chính vào thời điểm đó?"
  • "Tương tác đó có gây ra nhiều hoạt động kết xuất khiến khung hình tiếp theo bị trì hoãn không?"

Bảng sau đây cho thấy một số dữ liệu cơ bản về hoạt động phân bổ mà bạn có thể nhận được từ thư viện. Dữ liệu này có thể giúp bạn tìm ra một số nguyên nhân cấp cao gây ra các hoạt động tương tác chậm trên trang web của bạn:

Khoá đối tượng attribution Dữ liệu
interactionTarget Bộ chọn CSS trỏ đến phần tử tạo ra giá trị INP của trang, ví dụ: button#save.
interactionType Loại tương tác, có thể là lượt nhấp, lượt nhấn hoặc dữ liệu đầu vào từ bàn phím.
inputDelay* Độ trễ khi nhập của lượt tương tác.
processingDuration* Khoảng thời gian từ khi trình nghe sự kiện đầu tiên bắt đầu chạy để phản hồi hoạt động tương tác của người dùng cho đến khi tất cả quá trình xử lý trình nghe sự kiện hoàn tất.
presentationDelay* Độ trễ trình bày của hoạt động tương tác, diễn ra từ khi trình xử lý sự kiện hoàn tất đến thời điểm khung hình tiếp theo được vẽ.
longAnimationFrameEntries* Các mục trong LoAF được liên kết với lượt tương tác. Xem phần tiếp theo để biết thêm thông tin.
*Tính năng mới trong phiên bản 4

Kể từ phiên bản 4 của thư viện web-vitals, bạn có thể hiểu rõ hơn nữa về các lượt tương tác có vấn đề thông qua dữ liệu mà thư viện này cung cấp cùng với thông tin chi tiết về các giai đoạn INP (độ trễ đầu vào, thời lượng xử lý và độ trễ trình bày) và Long Animation Frames API (LoAF).

Long Animation Frames API (LoAF)

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: not supported.
  • Safari: not supported.

Source

Gỡ lỗi các lượt tương tác bằng dữ liệu trường là một việc khó khăn. Tuy nhiên, nhờ dữ liệu từ LoAF, giờ đây, bạn có thể hiểu rõ hơn về nguyên nhân dẫn đến các lượt tương tác diễn ra chậm, vì LoAF cho thấy một số thông tin chi tiết về thời gian và các dữ liệu khác mà bạn có thể dùng để xác định chính xác nguyên nhân – và quan trọng hơn là vị trí nguồn gốc của vấn đề trong mã của trang web.

Bản dựng phân bổ của thư viện web-vitals sẽ hiển thị một mảng các mục LoAF trong khoá longAnimationFrameEntries của đối tượng attribution. Bảng sau đây liệt kê một số dữ liệu chính mà bạn có thể tìm thấy trong mỗi mục nhập LoAF:

Khoá đối tượng mục LoAF Dữ liệu
duration Thời lượng của khung hình động dài, cho đến khi bố cục hoàn tất, nhưng không bao gồm quá trình vẽ và kết hợp.
blockingDuration Tổng thời gian trong khung hình mà trình duyệt không thể phản hồi nhanh do các tác vụ dài. Thời gian chặn này có thể bao gồm các tác vụ dài chạy JavaScript, cũng như mọi tác vụ kết xuất dài tiếp theo trong khung hình.
firstUIEventTimestamp Dấu thời gian của thời điểm sự kiện được đưa vào hàng đợi trong khung hình. Hữu ích khi tìm ra thời điểm bắt đầu độ trễ đầu vào của một lượt tương tác.
startTime Dấu thời gian bắt đầu của khung hình.
renderStart Thời điểm bắt đầu quá trình kết xuất cho khung hình. Điều này bao gồm mọi lệnh gọi lại requestAnimationFrame (và lệnh gọi lại ResizeObserver nếu có), nhưng có thể trước khi bắt đầu bất kỳ hoạt động nào về kiểu/bố cục.
styleAndLayoutStart Khi có hoạt động về kiểu/bố cục trong khung. Có thể hữu ích trong việc xác định thời lượng của công việc tạo kiểu/bố cục khi tính đến các dấu thời gian khác có sẵn.
scripts Một mảng các mục chứa thông tin phân bổ tập lệnh góp phần vào chỉ số INP của trang.
Hình ảnh minh hoạ một khung hình động dài theo mô hình LoAF.
Sơ đồ về thời gian của một khung hình ảnh động cần nhiều thời gian theo API LoAF (trừ blockingDuration).

Tất cả thông tin này có thể cho bạn biết nhiều điều về nguyên nhân khiến một lượt tương tác diễn ra chậm, nhưng mảng scripts mà các mục LoAF hiển thị sẽ đặc biệt hữu ích:

Khoá đối tượng phân bổ tập lệnh Dữ liệu
invoker Đối tượng gọi. Điều này có thể thay đổi tuỳ theo loại phương thức gọi được mô tả trong hàng tiếp theo. Ví dụ về đối tượng gọi có thể là các giá trị như 'IMG#id.onload', 'Window.requestAnimationFrame' hoặc 'Response.json.then'.
invokerType Loại đối tượng gọi. Có thể là 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script' hoặc 'module-script'.
sourceURL URL của tập lệnh nơi khung hoạt hoạ cần nhiều thời gian bắt nguồn.
sourceCharPosition Vị trí của ký tự trong tập lệnh do sourceURL xác định.
sourceFunctionName Tên của hàm trong tập lệnh được xác định.

Mỗi mục trong mảng này chứa dữ liệu xuất hiện trong bảng này, cung cấp cho bạn thông tin về tập lệnh chịu trách nhiệm cho hoạt động tương tác chậm và cách tập lệnh đó chịu trách nhiệm.

Đo lường và xác định các nguyên nhân thường gặp gây ra lượt tương tác chậm

Để giúp bạn hình dung cách sử dụng thông tin này, hướng dẫn này sẽ trình bày cách bạn có thể sử dụng dữ liệu LoAF xuất hiện trong thư viện web-vitals để xác định một số nguyên nhân gây ra các hoạt động tương tác diễn ra chậm.

Thời gian xử lý lâu

Thời lượng xử lý của một lượt tương tác là thời gian cần thiết để các lệnh gọi lại trình xử lý sự kiện đã đăng ký của lượt tương tác chạy cho đến khi hoàn tất và mọi thứ khác có thể xảy ra trong khoảng thời gian đó. Thư viện web-vitals sẽ hiển thị thời lượng xử lý cao:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5
});

Bạn có thể cho rằng nguyên nhân chính khiến hoạt động tương tác diễn ra chậm là do mã trình xử lý sự kiện của bạn mất quá nhiều thời gian để chạy, nhưng không phải lúc nào cũng như vậy! Sau khi xác nhận rằng đây là vấn đề, bạn có thể tìm hiểu sâu hơn bằng dữ liệu LoAF:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5

  // Get the longest script from LoAF covering `processingDuration`:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Get attribution for the long-running event handler:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Như bạn có thể thấy trong đoạn mã trước, bạn có thể sử dụng dữ liệu LoAF để theo dõi nguyên nhân chính xác đằng sau một lượt tương tác có giá trị thời lượng xử lý cao, bao gồm:

  • Phần tử và trình nghe sự kiện đã đăng ký của phần tử đó.
  • Tệp tập lệnh (và vị trí ký tự trong tệp đó) chứa mã trình xử lý sự kiện chạy trong thời gian dài.
  • Tên của hàm.

Loại dữ liệu này vô cùng quý giá. Bạn không còn cần phải tìm hiểu chính xác tương tác nào (hoặc trình xử lý sự kiện nào của tương tác đó) chịu trách nhiệm cho các giá trị thời lượng xử lý cao. Ngoài ra, vì tập lệnh của bên thứ ba thường có thể đăng ký trình xử lý sự kiện riêng, nên bạn có thể xác định xem mã của mình có chịu trách nhiệm hay không! Đối với mã mà bạn kiểm soát, bạn nên xem xét tối ưu hoá các tác vụ dài.

Độ trễ đầu vào dài

Mặc dù trình xử lý sự kiện chạy trong thời gian dài là điều thường thấy, nhưng bạn cần xem xét các phần khác của hoạt động tương tác. Một phần xảy ra trước thời gian xử lý, được gọi là độ trễ đầu vào. Đây là khoảng thời gian từ khi người dùng bắt đầu tương tác cho đến thời điểm các lệnh gọi lại của trình xử lý sự kiện bắt đầu chạy và xảy ra khi luồng chính đang xử lý một tác vụ khác. Bản dựng phân bổ của thư viện web-vitals có thể cho bạn biết độ dài của độ trễ đầu vào cho một lượt tương tác:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536
});

Nếu nhận thấy một số lượt tương tác có độ trễ đầu vào cao, thì bạn cần tìm hiểu xem điều gì đã xảy ra trên trang tại thời điểm diễn ra lượt tương tác gây ra độ trễ đầu vào dài. Điều này thường là do lượt tương tác xảy ra khi trang đang tải hoặc sau đó.

Lỗi có xảy ra trong quá trình tải trang không?

Luồng chính thường bận nhất khi một trang đang tải. Trong thời gian này, mọi loại tác vụ đều được đưa vào hàng đợi và xử lý. Nếu người dùng cố gắng tương tác với trang trong khi tất cả các thao tác này đang diễn ra, thì thao tác tương tác có thể bị chậm trễ. Những trang tải nhiều JavaScript có thể bắt đầu quá trình biên dịch và đánh giá tập lệnh, cũng như thực thi các hàm giúp trang sẵn sàng cho hoạt động tương tác của người dùng. Thao tác này có thể gây cản trở nếu người dùng tương tác khi hoạt động này diễn ra. Bạn có thể tìm hiểu xem người dùng trên trang web của mình có gặp phải trường hợp này hay không:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Invoker types can describe if script eval blocked the main thread:
    const {invokerType} = script;    // 'classic-script' | 'module-script'
    const {sourceLocation} = script; // 'https://example.com/app.js'
  }
});

Nếu bạn ghi lại dữ liệu này trong trường và thấy độ trễ đầu vào cao cũng như các loại lệnh gọi là 'classic-script' hoặc 'module-script', thì có thể nói rằng các tập lệnh trên trang web của bạn mất nhiều thời gian để đánh giá và đang chặn luồng chính đủ lâu để làm chậm các lượt tương tác. Bạn có thể giảm thời gian chặn này bằng cách chia tập lệnh thành các gói nhỏ hơn, hoãn tải mã không dùng đến ban đầu vào thời điểm sau này và kiểm tra trang web của bạn để tìm mã không dùng đến mà bạn có thể xoá hoàn toàn.

Có phải sau khi tải trang không?

Mặc dù độ trễ đầu vào thường xảy ra trong khi một trang đang tải, nhưng cũng có thể xảy ra sau khi một trang đã tải, do một nguyên nhân hoàn toàn khác. Các nguyên nhân thường gặp gây ra độ trễ đầu vào sau khi tải trang có thể là mã chạy định kỳ do lệnh gọi setInterval trước đó hoặc thậm chí là các lệnh gọi lại sự kiện được xếp hàng để chạy trước đó và vẫn đang xử lý.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];

  if (script) {
    const {invokerType} = script;        // 'user-callback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Tương tự như trường hợp khắc phục sự cố về các giá trị thời gian xử lý cao, độ trễ đầu vào cao do các nguyên nhân đã đề cập trước đó sẽ cung cấp cho bạn dữ liệu phân bổ tập lệnh chi tiết. Tuy nhiên, điểm khác biệt là loại đối tượng gọi sẽ thay đổi dựa trên bản chất của công việc làm chậm quá trình tương tác:

  • 'user-callback' cho biết tác vụ chặn đến từ setInterval, setTimeout hoặc thậm chí là requestAnimationFrame.
  • 'event-listener' cho biết tác vụ chặn là do một đầu vào trước đó được đưa vào hàng đợi và vẫn đang xử lý.
  • 'resolve-promise''reject-promise' có nghĩa là tác vụ chặn đến từ một số hoạt động không đồng bộ đã được bắt đầu trước đó và được giải quyết hoặc bị từ chối vào thời điểm người dùng cố gắng tương tác với trang, làm chậm quá trình tương tác.

Trong mọi trường hợp, dữ liệu phân bổ tập lệnh sẽ cho bạn biết nên bắt đầu tìm kiếm ở đâu và liệu độ trễ đầu vào là do mã của riêng bạn hay do tập lệnh của bên thứ ba.

Độ trễ trình chiếu dài

Độ trễ khi trình bày là chặng cuối của một lượt tương tác, bắt đầu khi các trình xử lý sự kiện của lượt tương tác kết thúc, cho đến thời điểm khung hình tiếp theo được vẽ. Các thay đổi này xảy ra khi thao tác trong một trình xử lý sự kiện do một hoạt động tương tác làm thay đổi trạng thái trực quan của giao diện người dùng. Tương tự như thời lượng xử lý và độ trễ đầu vào, thư viện web-vitals có thể cho bạn biết độ trễ trình bày của một lượt tương tác là bao lâu:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691
});

Nếu bạn ghi lại dữ liệu này và thấy độ trễ trình bày cao đối với các lượt tương tác đóng góp vào chỉ số INP của trang web, thì nguyên nhân có thể rất đa dạng, nhưng bạn cần lưu ý một số nguyên nhân sau.

Tốn nhiều công sức cho kiểu và bố cục

Độ trễ trình bày dài có thể là do tính toán lại kiểubố cục tốn kém phát sinh từ một số nguyên nhân, bao gồm cả bộ chọn CSS phức tạp và kích thước DOM lớn. Bạn có thể đo lường khoảng thời gian của hoạt động này bằng cách sử dụng các thời gian LoAF xuất hiện trong thư viện web-vitals:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];

  // Get necessary timings:
  const {startTime} = loaf; // 2120.5
  const {duration} = loaf;  // 1002

  // Figure out the ending timestamp of the frame (approximate):
  const endTime = startTime + duration; // 3122.5

  // Get the start timestamp of the frame's style/layout work:
  const {styleAndLayoutStart} = loaf; // 3011.17692309

  // Calculate the total style/layout duration:
  const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running style and layout operation:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

LoAF sẽ không cho bạn biết thời lượng của công việc về kiểu và bố cục cho một khung hình, nhưng sẽ cho bạn biết thời điểm bắt đầu. Với dấu thời gian bắt đầu này, bạn có thể sử dụng các dữ liệu khác từ LoAF để tính toán chính xác thời lượng của hoạt động đó bằng cách xác định thời gian kết thúc của khung hình, rồi trừ dấu thời gian bắt đầu của hoạt động tạo kiểu và bố cục.

Lệnh gọi lại requestAnimationFrame kéo dài

Một nguyên nhân tiềm ẩn gây ra tình trạng chậm trễ khi trình bày là do có quá nhiều việc cần làm trong lệnh gọi lại requestAnimationFrame. Nội dung của lệnh gọi lại này được thực thi sau khi trình xử lý sự kiện hoàn tất quá trình chạy, nhưng ngay trước khi tính toán lại kiểu và bố cục.

Các lệnh gọi lại này có thể mất nhiều thời gian để hoàn tất nếu công việc được thực hiện trong đó phức tạp. Nếu nghi ngờ giá trị độ trễ hiển thị cao là do công việc bạn đang thực hiện với requestAnimationFrame, bạn có thể sử dụng dữ liệu LoAF do thư viện web-vitals cung cấp để xác định những trường hợp này:

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 543.1999999880791

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];

  // Get the render start time and when style and layout began:
  const {renderStart} = loaf;         // 2489
  const {styleAndLayoutStart} = loaf; // 2989.5999999940395

  // Calculate the `requestAnimationFrame` callback's duration:
  const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running requestAnimationFrame callback:
    const {invokerType} = script;        // 'user-callback'
    const {invoker} = script;            // 'FrameRequestCallback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Nếu bạn thấy một phần đáng kể thời gian trễ trình bày được dùng trong một lệnh gọi lại requestAnimationFrame, hãy đảm bảo rằng công việc bạn đang thực hiện trong các lệnh gọi lại này chỉ giới hạn ở việc thực hiện công việc dẫn đến việc cập nhật thực tế cho giao diện người dùng. Bất kỳ thao tác nào khác không liên quan đến DOM hoặc không cập nhật kiểu sẽ trì hoãn không cần thiết việc vẽ khung hình tiếp theo, vì vậy hãy cẩn thận!

Kết luận

Dữ liệu thực tế là nguồn thông tin tốt nhất mà bạn có thể dựa vào khi muốn biết những hoạt động tương tác nào gây ra vấn đề cho người dùng thực tế. Bằng cách dựa vào các công cụ thu thập dữ liệu thực tế như thư viện JavaScript web-vitals (hoặc nhà cung cấp RUM), bạn có thể tự tin hơn về những hoạt động tương tác có nhiều vấn đề nhất, sau đó chuyển sang tái tạo các hoạt động tương tác có vấn đề trong phòng thí nghiệm rồi tiến hành khắc phục.

Hình ảnh chính trên Unsplash, của Federico Respini.