Tối ưu hoá tương tác cho thời gian hiển thị tiếp theo

Tìm hiểu cách tối ưu hoá chỉ số Lượt tương tác đến nội dung hiển thị tiếp theo của trang web.

Xuất bản: Ngày 19 tháng 5 năm 2023, Lần cập nhật gần đây nhất: Ngày 2 tháng 9 năm 2025

Lượt tương tác đến nội dung hiển thị tiếp theo (INP) là một chỉ số ổn định trong Core Web Vitals. Chỉ số này đánh giá khả năng phản hồi tổng thể của một trang đối với hoạt động tương tác của người dùng bằng cách quan sát độ trễ của tất cả lượt tương tác đủ điều kiện xảy ra trong suốt thời gian người dùng truy cập vào một trang. Giá trị INP cuối cùng tương ứng với lượt tương tác dài nhất quan sát được (đôi khi bỏ qua các điểm ngoại lai).

Để mang lại trải nghiệm tốt cho người dùng, các trang web nên cố gắng đạt được chỉ số Lượt tương tác đến nội dung hiển thị tiếp theo là 200 mili giây trở xuống. Để đạt được mục tiêu này cho hầu hết người dùng, ngưỡng phù hợp để đo lường là phân vị thứ 75 của số lượt tải trang, được phân đoạn trên thiết bị di động và máy tính.

Giá trị INP tốt là 200 mili giây trở xuống, giá trị kém là trên 500 mili giây và giá trị nằm giữa hai mức này cần được cải thiện.
Ngưỡng INP

Tuỳ thuộc vào trang web, có thể có ít hoặc không có hoạt động tương tác, chẳng hạn như các trang chủ yếu là văn bản và hình ảnh, có ít hoặc không có thành phần tương tác. Hoặc trong trường hợp các trang web như trình chỉnh sửa văn bản hoặc trò chơi, có thể có hàng trăm, thậm chí hàng nghìn lượt tương tác. Trong cả hai trường hợp, khi INP cao, trải nghiệm người dùng sẽ gặp rủi ro.

Việc cải thiện INP cần có thời gian và công sức, nhưng phần thưởng là trải nghiệm người dùng tốt hơn. Trong hướng dẫn này, chúng ta sẽ tìm hiểu một lộ trình cải thiện INP.

Tìm hiểu nguyên nhân khiến chỉ số INP kém

Trước khi có thể khắc phục các lượt tương tác chậm, bạn cần có dữ liệu để biết liệu INP của trang web có kém hoặc cần cải thiện hay không. Sau khi có thông tin đó, bạn có thể chuyển sang phòng thí nghiệm để bắt đầu chẩn đoán các lượt tương tác chậm và tìm ra giải pháp.

Tìm các lượt tương tác chậm trong trường

Tốt nhất là hành trình tối ưu hoá INP của bạn nên bắt đầu bằng dữ liệu thực địa. Tốt nhất là dữ liệu thực địa từ một nhà cung cấp dịch vụ Giám sát người dùng thực (RUM) sẽ không chỉ cung cấp cho bạn giá trị INP của trang mà còn cung cấp dữ liệu bối cảnh làm nổi bật lượt tương tác cụ thể nào chịu trách nhiệm về chính giá trị INP, cho dù lượt tương tác đó xảy ra trong hay sau khi tải trang, loại tương tác (nhấp, nhấn phím hoặc nhấn) và các thông tin có giá trị khác.

Nếu bạn không dựa vào nhà cung cấp RUM để lấy dữ liệu thực tế, thì hướng dẫn về dữ liệu thực tế của INP khuyên bạn nên sử dụng PageSpeed Insights để xem dữ liệu Báo cáo trải nghiệm người dùng trên Chrome (CrUX) nhằm giúp lấp đầy khoảng trống. CrUX là tập dữ liệu của Google về chương trình Các chỉ số quan trọng về trang web, đồng thời cung cấp thông tin tóm tắt tổng quan về các chỉ số cho hàng triệu trang web, bao gồm cả INP. Tuy nhiên, CrUX thường không cung cấp dữ liệu bối cảnh mà bạn nhận được từ nhà cung cấp RUM để giúp bạn phân tích vấn đề. Do đó, bạn vẫn nên sử dụng nhà cung cấp RUM khi có thể hoặc triển khai giải pháp RUM của riêng mình để bổ sung cho những gì có trong CrUX.

Chẩn đoán các lượt tương tác chậm trong phòng thí nghiệm

Tốt nhất là bạn nên bắt đầu thử nghiệm trong phòng thí nghiệm sau khi có dữ liệu thực địa cho thấy bạn có các lượt tương tác chậm. Nếu không có dữ liệu thực tế, bạn có thể áp dụng một số chiến lược để xác định các lượt tương tác chậm trong phòng thí nghiệm. Các chiến lược như vậy bao gồm việc theo dõi luồng người dùng phổ biến và kiểm thử các hoạt động tương tác trong suốt quá trình, cũng như tương tác với trang trong khi tải (khi luồng chính thường bận rộn nhất) để xác định các hoạt động tương tác chậm trong phần quan trọng đó của trải nghiệm người dùng.

Tối ưu hoá lượt tương tác

Sau khi xác định được một lượt tương tác chậm và có thể tái hiện lượt tương tác đó theo cách thủ công trong phòng thí nghiệm, bước tiếp theo là tối ưu hoá lượt tương tác đó.

Tương tác có thể được chia thành ba phần phụ:

  1. Độ trễ đầu vào, bắt đầu khi người dùng bắt đầu tương tác với trang và kết thúc khi lệnh gọi lại sự kiện cho hoạt động tương tác bắt đầu chạy.
  2. Thời gian xử lý, bao gồm thời gian cần thiết để các lệnh gọi lại sự kiện chạy đến khi hoàn tất.
  3. Độ trễ hiển thị, tức là thời gian cần thiết để trình duyệt hiển thị khung hình tiếp theo chứa kết quả trực quan của lượt tương tác.
Ví dụ về một lượt tương tác trên luồng chính. Người dùng nhập dữ liệu trong khi các tác vụ chặn đang chạy. Dữ liệu đầu vào bị trì hoãn cho đến khi các tác vụ đó hoàn tất, sau đó trình xử lý sự kiện pointerup, mouseup và sự kiện nhấp chuột sẽ chạy, sau đó công việc kết xuất và tạo điểm ảnh được bắt đầu cho đến khi khung tiếp theo được hiển thị.
Vòng đời của một lượt tương tác. Độ trễ đầu vào xảy ra cho đến khi trình xử lý sự kiện bắt đầu chạy, có thể do các yếu tố như tác vụ cần nhiều thời gian trên luồng chính. Sau đó, lệnh gọi lại trình xử lý sự kiện của hoạt động tương tác sẽ chạy và xảy ra độ trễ trước khi khung hình tiếp theo xuất hiện.

Tổng của ba phần phụ này là tổng độ trễ tương tác. Mỗi phần phụ của một lượt tương tác đều đóng góp một khoảng thời gian vào tổng độ trễ của lượt tương tác. Vì vậy, bạn cần biết cách tối ưu hoá từng phần của lượt tương tác để lượt tương tác đó chạy trong thời gian ngắn nhất có thể.

Xác định và giảm độ trễ đầu vào

Khi người dùng tương tác với một trang, phần đầu tiên của lượt tương tác đó là độ trễ đầu vào. Tuỳ thuộc vào hoạt động khác trên trang, độ trễ đầu vào có thể khá dài. Điều này có thể là do hoạt động xảy ra trên luồng chính (có thể là do tập lệnh tải, phân tích cú pháp và biên dịch), xử lý tìm nạp, hàm hẹn giờ hoặc thậm chí là do các hoạt động tương tác khác xảy ra liên tiếp và chồng chéo lên nhau.

Dù độ trễ đầu vào của một lượt tương tác bắt nguồn từ đâu, bạn cũng nên giảm độ trễ đầu vào xuống mức tối thiểu để các lượt tương tác có thể bắt đầu chạy lệnh gọi lại sự kiện càng sớm càng tốt.

Mối quan hệ giữa việc đánh giá tập lệnh và các tác vụ cần nhiều thời gian trong quá trình khởi động

Một khía cạnh quan trọng của khả năng tương tác trong vòng đời trang là trong quá trình khởi động. Khi tải, trang sẽ hiển thị ban đầu, nhưng điều quan trọng cần nhớ là việc trang đã hiển thị không có nghĩa là trang đã tải xong. Tuỳ thuộc vào số lượng tài nguyên mà một trang cần để hoạt động đầy đủ, người dùng có thể cố gắng tương tác với trang trong khi trang vẫn đang tải.

Một yếu tố có thể kéo dài độ trễ đầu vào của một lượt tương tác trong khi tải trang là việc đánh giá tập lệnh. Sau khi tìm nạp tệp JavaScript từ mạng, trình duyệt vẫn còn việc phải làm trước khi JavaScript đó có thể chạy; công việc đó bao gồm phân tích cú pháp tập lệnh để kiểm tra cú pháp có hợp lệ hay không, biên dịch tập lệnh thành mã byte rồi cuối cùng là thực thi tập lệnh.

Tuỳ thuộc vào kích thước của tập lệnh, công việc này có thể tạo ra các tác vụ cần nhiều thời gian trên luồng chính, khiến trình duyệt chậm phản hồi các hoạt động tương tác khác của người dùng. Để trang luôn thích ứng với hoạt động đầu vào của người dùng trong quá trình tải trang, bạn cần hiểu rõ những việc có thể làm để giảm khả năng xảy ra các tác vụ cần nhiều thời gian trong quá trình tải trang, nhờ đó trang luôn hoạt động nhanh chóng.

Tối ưu hoá lệnh gọi lại sự kiện

Độ trễ đầu vào chỉ là phần đầu tiên trong những gì INP đo lường. Bạn cũng cần đảm bảo rằng các lệnh gọi lại sự kiện chạy để phản hồi hoạt động tương tác của người dùng có thể hoàn tất nhanh nhất có thể.

Thường xuyên nhường quyền cho luồng chính

Lời khuyên chung hay nhất để tối ưu hoá lệnh gọi lại sự kiện là thực hiện càng ít công việc càng tốt trong các lệnh gọi lại đó. Tuy nhiên, logic tương tác của bạn có thể phức tạp và bạn chỉ có thể giảm bớt một phần công việc mà các luồng này thực hiện.

Nếu thấy trang web của mình gặp phải trường hợp này, thì việc tiếp theo bạn có thể thử là chia nhỏ công việc trong lệnh gọi lại sự kiện thành các tác vụ riêng biệt. Điều này giúp ngăn công việc chung trở thành một tác vụ cần nhiều thời gian chặn luồng chính, cho phép các hoạt động tương tác khác (vốn sẽ phải chờ trên luồng chính) chạy sớm hơn.

setTimeout là một cách để chia nhỏ các tác vụ, vì lệnh gọi lại được truyền đến hàm này sẽ chạy trong một tác vụ mới. Bạn có thể sử dụng setTimeout riêng lẻ hoặc trừu tượng hoá việc sử dụng hàm này thành một hàm riêng biệt để mang lại hiệu quả cao hơn.

Việc nhường quyền một cách bừa bãi sẽ tốt hơn là không nhường quyền. Tuy nhiên, có một cách nhường quyền tinh tế hơn cho luồng chính, đó là chỉ nhường quyền ngay sau lệnh gọi lại sự kiện cập nhật giao diện người dùng để logic hiển thị có thể chạy sớm hơn.

Nhường quyền để công việc kết xuất diễn ra sớm hơn

Một kỹ thuật nhường quyền nâng cao hơn là cấu trúc mã trong các lệnh gọi lại sự kiện để giới hạn những gì được chạy chỉ ở logic cần thiết để áp dụng các bản cập nhật hình ảnh cho khung hình tiếp theo. Mọi thứ khác có thể được trì hoãn cho một nhiệm vụ tiếp theo. Điều này không chỉ giúp các lệnh gọi lại nhẹ và linh hoạt mà còn cải thiện thời gian kết xuất cho các lượt tương tác bằng cách không cho phép các bản cập nhật hình ảnh chặn mã lệnh gọi lại sự kiện.

Ví dụ: hãy tưởng tượng một trình chỉnh sửa văn bản đa dạng thức định dạng văn bản khi bạn nhập, nhưng cũng cập nhật các khía cạnh khác của giao diện người dùng để phản hồi nội dung bạn đã viết (chẳng hạn như số từ, làm nổi bật lỗi chính tả và các phản hồi trực quan quan trọng khác). Ngoài ra, ứng dụng cũng có thể cần lưu nội dung bạn đã viết để nếu bạn rời đi và quay lại, bạn sẽ không bị mất bất kỳ nội dung nào.

Trong ví dụ này, 4 điều sau đây cần xảy ra để phản hồi các ký tự do người dùng nhập. Tuy nhiên, chỉ mục đầu tiên cần được thực hiện trước khi khung tiếp theo xuất hiện.

  1. Cập nhật hộp văn bản bằng nội dung người dùng đã nhập và áp dụng mọi định dạng bắt buộc.
  2. Cập nhật phần giao diện người dùng hiển thị số từ hiện tại.
  3. Chạy logic để kiểm tra lỗi chính tả.
  4. Lưu các thay đổi gần đây nhất (trên thiết bị hoặc vào cơ sở dữ liệu từ xa).

Mã để thực hiện việc này có thể có dạng như sau:

textBox.addEventListener('input', (inputEvent) => {
  // Update the UI immediately, so the changes the user made
  // are visible as soon as the next frame is presented.
  updateTextBox(inputEvent);

  // Use `setTimeout` to defer all other work until at least the next
  // frame by queuing a task in a `requestAnimationFrame()` callback.
  requestAnimationFrame(() => {
    setTimeout(() => {
      const text = textBox.textContent;
      updateWordCount(text);
      checkSpelling(text);
      saveChanges(text);
    }, 0);
  });
});

Hình ảnh sau đây cho thấy cách trì hoãn mọi nội dung cập nhật không quan trọng cho đến sau khung hình tiếp theo có thể giảm thời gian xử lý và do đó giảm độ trễ tương tác tổng thể.

Hình minh hoạ một hoạt động tương tác với bàn phím và các tác vụ tiếp theo trong hai trường hợp. Trong hình trên, tác vụ quan trọng đối với việc kết xuất và tất cả tác vụ nền tiếp theo đều chạy đồng bộ cho đến khi có cơ hội hiển thị khung hình. Trong hình dưới, công việc quan trọng đối với quá trình kết xuất sẽ chạy trước, sau đó nhường chỗ cho luồng chính để hiển thị khung hình mới sớm hơn. Sau đó, các tác vụ trong nền sẽ chạy.
Nhấp vào hình để xem phiên bản có độ phân giải cao.

Mặc dù việc sử dụng setTimeout() bên trong lệnh gọi requestAnimationFrame() trong ví dụ về mã trước đó có thể hơi khó hiểu, nhưng đây là một phương thức hiệu quả hoạt động trên tất cả trình duyệt để ngăn mã không quan trọng chặn khung hình tiếp theo.

Tránh tình trạng đơ bố cục

Layout thrashing (đôi khi được gọi là bố cục đồng bộ bắt buộc) là một vấn đề về hiệu suất kết xuất, trong đó bố cục xảy ra đồng bộ. Tình trạng này xảy ra khi bạn cập nhật kiểu trong JavaScript, sau đó đọc kiểu trong cùng một tác vụ. Có nhiều thuộc tính trong JavaScript có thể gây ra tình trạng dồn dập bố cục.

Hình ảnh minh hoạ về tình trạng dồn dập bố cục như trong bảng điều khiển hiệu suất của Chrome DevTools.
Ví dụ về tình trạng dồn dập bố cục, như trong bảng điều khiển hiệu suất của Chrome DevTools. Các tác vụ kết xuất liên quan đến việc dồn dập bố cục sẽ được đánh dấu bằng một hình tam giác màu đỏ ở góc trên bên phải của phần ngăn xếp lệnh gọi, thường được gắn nhãn Recalculate Style (Tính toán lại kiểu) hoặc Layout (Bố cục).

Tình trạng dồn dập bố cục là một điểm tắc nghẽn về hiệu suất vì bằng cách cập nhật kiểu rồi yêu cầu ngay giá trị của các kiểu đó trong JavaScript, trình duyệt buộc phải thực hiện công việc bố cục đồng bộ mà lẽ ra có thể đợi để thực hiện không đồng bộ sau đó, sau khi các lệnh gọi lại sự kiện chạy xong.

Giảm thiểu độ trễ khi trình bày

Độ trễ hiển thị của một lượt tương tác kéo dài từ thời điểm lệnh gọi lại sự kiện của một lượt tương tác chạy xong cho đến thời điểm trình duyệt có thể vẽ khung hình tiếp theo cho thấy các thay đổi về hình ảnh.

Giảm thiểu kích thước DOM

Khi DOM của trang nhỏ, công việc kết xuất thường hoàn tất nhanh chóng. Tuy nhiên, khi DOM trở nên rất lớn, công việc kết xuất có xu hướng tăng theo kích thước DOM ngày càng tăng. Mối quan hệ giữa công việc kết xuất và kích thước DOM không phải là mối quan hệ tuyến tính, nhưng DOM lớn cần nhiều công việc hơn để kết xuất so với DOM nhỏ. DOM lớn có vấn đề trong hai trường hợp:

  1. Trong quá trình kết xuất trang ban đầu, khi một DOM lớn cần nhiều công việc để kết xuất trạng thái ban đầu của trang.
  2. Để phản hồi hoạt động tương tác của người dùng, trong đó DOM lớn có thể khiến việc cập nhật kết xuất trở nên rất tốn kém, từ đó làm tăng thời gian trình duyệt cần để hiển thị khung hình tiếp theo.

Xin lưu ý rằng có những trường hợp không thể giảm đáng kể DOM lớn. Mặc dù có những phương pháp bạn có thể áp dụng để giảm kích thước DOM, chẳng hạn như làm phẳng DOM hoặc thêm vào DOM trong quá trình tương tác của người dùng để giữ cho kích thước DOM ban đầu ở mức nhỏ, nhưng những kỹ thuật đó có thể chỉ có tác dụng ở một mức độ nhất định.

Sử dụng content-visibility để hiển thị từng phần tử ngoài màn hình

Một cách để bạn có thể giới hạn cả lượng công việc kết xuất trong quá trình tải trang và công việc kết xuất để phản hồi hoạt động tương tác của người dùng là dựa vào thuộc tính CSS content-visibility. Thuộc tính này giúp kết xuất từng phần các phần tử khi chúng tiếp cận khung nhìn. Mặc dù bạn có thể cần thực hành một chút để sử dụng content-visibility một cách hiệu quả, nhưng bạn nên tìm hiểu xem kết quả có phải là thời gian kết xuất thấp hơn có thể cải thiện INP của trang hay không.

Lưu ý đến chi phí hiệu suất khi kết xuất HTML bằng JavaScript

Khi có HTML, quá trình phân tích cú pháp HTML sẽ diễn ra. Sau khi trình duyệt phân tích cú pháp HTML thành DOM, trình duyệt phải áp dụng kiểu cho DOM, thực hiện các phép tính bố cục và sau đó kết xuất bố cục đó. Đây là chi phí không thể tránh khỏi, nhưng cách bạn kết xuất HTML mới là điều quan trọng.

Khi máy chủ gửi HTML, HTML sẽ đến trình duyệt dưới dạng một luồng. Truyền trực tuyến có nghĩa là phản hồi HTML từ máy chủ sẽ đến theo từng phần. Trình duyệt tối ưu hoá cách xử lý một luồng bằng cách phân tích cú pháp dần dần các đoạn của luồng đó khi chúng đến và hiển thị từng chút một. Đây là một tính năng tối ưu hoá hiệu suất, trong đó trình duyệt sẽ tự động và định kỳ nhường quyền kiểm soát trong quá trình tải trang, và bạn không cần phải làm gì cả.

Mặc dù lượt truy cập đầu tiên vào bất kỳ trang web nào cũng luôn liên quan đến một lượng HTML nhất định, nhưng phương pháp phổ biến là bắt đầu bằng một lượng HTML ban đầu tối thiểu, sau đó JavaScript được dùng để điền nội dung vào vùng nội dung. Các bản cập nhật tiếp theo cho vùng nội dung đó cũng xảy ra do hoạt động tương tác của người dùng. Mô hình này thường được gọi là mô hình ứng dụng trang đơn (SPA). Một nhược điểm của mẫu này là khi kết xuất HTML bằng JavaScript trên máy khách, bạn không chỉ phải chịu chi phí xử lý JavaScript để tạo HTML đó, mà trình duyệt cũng sẽ không trả về kết quả cho đến khi phân tích cú pháp xong HTML đó và kết xuất.

Tuy nhiên, bạn cần nhớ rằng ngay cả những trang web không phải là SPA cũng có thể liên quan đến một số hoạt động kết xuất HTML thông qua JavaScript do các hoạt động tương tác. Điều này thường không có vấn đề gì, miễn là bạn không kết xuất lượng lớn HTML trên ứng dụng, vì việc này có thể làm chậm quá trình trình bày khung hình tiếp theo. Tuy nhiên, bạn cần hiểu rõ tác động của phương pháp này đối với hiệu suất khi kết xuất HTML trong trình duyệt, cũng như cách phương pháp này có thể ảnh hưởng đến khả năng phản hồi của trang web đối với hoạt động đầu vào của người dùng nếu bạn đang kết xuất nhiều HTML bằng JavaScript.

Kết luận

Cải thiện INP của trang web là một quá trình lặp lại. Khi khắc phục một lượt tương tác chậm trong thực tế, rất có thể bạn sẽ bắt đầu tìm thấy các lượt tương tác chậm khác (đặc biệt là nếu trang web của bạn cung cấp nhiều tính tương tác) và bạn cũng cần tối ưu hoá các lượt tương tác đó.

Chìa khoá để cải thiện INP là tính bền bỉ. Theo thời gian, bạn có thể cải thiện khả năng thích ứng của trang đến mức người dùng hài lòng với trải nghiệm mà bạn cung cấp cho họ. Cũng có thể khi phát triển các tính năng mới cho người dùng, bạn sẽ cần thực hiện quy trình tương tự để tối ưu hoá các hoạt động tương tác dành riêng cho họ. Việc này sẽ tốn thời gian và công sức, nhưng hoàn toàn xứng đáng.