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 hóa hoạt động tương tác với nội dung hiển thị tiếp theo trên trang web của bạn.

Lượt tương tác với Nội dung hiển thị tiếp theo (INP) là một chỉ số Chỉ số quan trọng chính của trang web ổn định, giúp đánh giá khả năng thích ứng tổng thể của một trang đối với các lượt tương tác của người dùng bằng cách quan sát độ trễ của tất cả cá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 là tương tác lâu nhất quan sát được (đôi khi bỏ qua các điểm ngoại lai).

Để cung cấp trải nghiệm tốt cho người dùng, các trang web nên cố gắng đảm bảo thời lượng Tương tác đến thời lượng hiển thị tiếp theo là 200 mili giây trở xuống. Để đảm bảo bạn đạt được mục tiêu này cho hầu hết người dùng của mình, một ngưỡng tốt để đ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 để bàn.

Giá trị INP tốt là 200 mili giây trở xuống, giá trị kém lớn hơn 500 mili giây và những giá trị còn lại cần cải thiện.

Tuỳ thuộc vào trang web, có thể có ít hoặc không có lượt tương tác nào, chẳng hạn như các trang chủ yếu là văn bản và hình ảnh với í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 hay trò chơi, có thể có hàng trăm—thậm chí hàng nghìn—tương tác. Trong cả hai trường hợp, khi có INP cao, trải nghiệm người dùng đều gặp rủi ro.

Cần có thời gian và công sức để cải thiện INP, nhưng phần thưởng chính là trải nghiệm người dùng tốt hơn. Trong hướng dẫn này, bạn sẽ khám phá lộ trình cải thiện INP.

Tìm hiểu nguyên nhân gây ra INP kém

Trước khi có thể khắc phục tình trạng tương tác chậm, bạn cần có dữ liệu để biết INP của trang web có kém hay cần cải thiện 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 tình huống tương tác chậm và tìm hướng giải quyết.

Tìm các 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 sẽ bắt đầu từ dữ liệu thực địa. Tốt nhất là dữ liệu thực địa từ nhà cung cấp dịch vụ Giám sát người dùng thực tế (rum) sẽ cung cấp cho bạn không chỉ giá trị INP của trang, mà còn cả dữ liệu ngữ cảnh nêu bật lượt tương tác cụ thể nào chịu trách nhiệm về giá trị INP, liệu lượt tương tác xảy ra trong hay sau khi tải trang, loại tương tác (lượt nhấp, thao tác nhấn 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 địa, hướng dẫn về dữ liệu trường của INP khuyên bạn nên sử dụng Báo cáo trải nghiệm người dùng trên Chrome (CrUX) thông qua PageSpeed Insights để bổ sung dữ liệu còn thiếu. CrUX là tập dữ liệu chính thức của chương trình Các chỉ số quan trọng về trang web và cung cấp bản tóm tắt cấp cao 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 theo ngữ cảnh mà bạn nhận được từ nhà cung cấp rum để giúp bạn phân tích các vấn đề. Do đó, các trang web vẫn nên sử dụng một nhà cung cấp rum hoặc triển khai giải pháp rum của riêng họ để bổ sung những dữ liệu có trong CrUX.

Chẩn đoán các 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 tế cho thấy bạn có mức độ tương tác chậm. Khi không có dữ liệu thực địa, có một số chiến lược để xác định tương tác chậm trong phòng thí nghiệm. Các chiến lược đó bao gồm theo dõi luồng người dùng phổ biến và tương tác thử nghiệm trong 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) để hiển thị các 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 một hành động tương tác chậm và có thể tái tạo hành động 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á tương tác đó. Hoạt động tương tác có thể chia thành ba giai đoạn:

  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 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 để chạy lệnh gọi lại sự kiện cho đến khi hoàn tất.
  3. Độ trễ hiển thị, là thời gian mà trình duyệt cần để hiển thị khung tiếp theo chứa kết quả trực quan của hoạt động tương tác.

Tổng của 3 giai đoạn này là tổng độ trễ tương tác. Mỗi giai đoạn tương tác đều đóng góp một khoảng thời gian vào tổng độ trễ tương tác, vì vậy, điều quan trọng là bạn phải biết cách tối ưu hoá từng phần của hoạt động tương tác để hoạt động đó 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ễ nhập có thể kéo dài đáng kể. Điều này có thể là do hoạt động xảy ra trên luồng chính (có thể là do việc tải, phân tích cú pháp và biên dịch tập lệnh), xử lý tìm nạp, các hàm bộ tính giờ hoặc thậm chí từ các hoạt động tương tác khác xảy ra liên tiếp và chồng chéo nhau.

Bất kể nguồn gây ra độ trễ đầu vào là gì, bạn đều cầ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 sớm nhất có thể.

Mối quan hệ giữa việc đánh giá tập lệnh và các tác vụ dài trong quá trình khởi động

Một khía cạnh quan trọng của tính tương tác trong vòng đời của trang là trong quá trình khởi động. Khi một trang được tải, ban đầu nó sẽ hiển thị. Tuy nhiên, điều quan trọng cần nhớ là chỉ vì một 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 với đầy đủ chức năng, có thể người dùng sẽ tìm cách tương tác với trang trong khi trang vẫn đang tải.

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

Tuỳ thuộc vào kích thước của một tập lệnh, tác vụ này có thể đưa ra các thao tác dài trên luồng chính, khiến trình duyệt không phản hồi các hoạt động tương tác khác của người dùng. Để đảm bảo trang của bạ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, điều quan trọng là bạn phải hiểu mình có thể làm gì để giảm khả năng người dùng thực hiện các thao tác dài trong khi tải trang để trang luôn nhanh chóng.

Lệnh gọi lại sự kiện Optimize

Độ trễ đầu vào chỉ là phần đầu tiên của giá trị đo lường INP. 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 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 chuyển đến luồng chính

Lời khuyên chung tốt nhất trong việc tối ưu hoá lệnh gọi lại sự kiện là bạn nên thực hiện càng ít thao tác càng tốt trong lệnh gọi lại này. 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 nhẹ công việc mà chúng thực hiện.

Nếu thấy trường hợp này xảy ra với trang web của bạn, bạn có thể thử 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 ngăn tác vụ tập thể trở thành một tác vụ dài chặn luồng chính, cho phép các tương tác khác vốn đang 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 nó chạy trong một tác vụ mới. Bạn có thể tự sử dụng setTimeout hoặc tóm tắt việc sử dụng thành một hàm riêng để tạo ra lợi nhuận hiệu quả hơn.

Việc cung cấp bừa bãi sẽ tốt hơn là không mang lại hiệu quả. Tuy nhiên, vẫn có một cách tinh vi hơn để tạo luồng chính và chỉ liên quan đến việc tạo ra ngay sau khi một lệnh gọi lại sự kiện cập nhật giao diện người dùng để logic kết xuất có thể chạy sớm hơn.

Lợi nhuận để cho phép công việc kết xuất diễn ra sớm hơn

Kỹ thuật tạo lợi nhuận nâng cao hơn bao gồm việc tạo 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ập nhật hình ảnh cho khung tiếp theo. Mọi thứ khác có thể bị trì hoãn đến tác vụ tiếp theo. Việc này không chỉ giúp các lệnh gọi lại trở nên gọn nhẹ và linh hoạt, mà còn cải thiện thời gian kết xuất hình ảnh tương tác bằng cách không cho phép cập nhật hình ảnh chặn mã 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 có thể định dạng văn bản khi bạn nhập nhưng đồng thời 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 những gì bạn đã viết (chẳng hạn như số từ, đánh dấu lỗi chính tả và phản hồi quan trọng bằng hình ảnh). Ngoài ra, ứng dụng cũng có thể cần lưu những gì bạn đã viết để nếu bạn rời đi và trở lại, bạn 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 hoàn tất trước khi khung tiếp theo hiển thị.

  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 (cục bộ hoặc cơ sở dữ liệu từ xa).

Mã để thực hiện việc này có thể giố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 việc trì hoãn mọi bản cập nhật không quan trọng cho đến khung hình tiếp theo có thể giúp giảm thời gian xử lý và từ đó làm giảm độ trễ tổng thể khi tương tác.

Nội dung mô tả 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 tình huống. Trong hình trên cùng, tác vụ quan trọng về kết xuất và tất cả tác vụ tiếp theo trong nền sẽ chạy đồng bộ cho đến khi có cơ hội hiển thị khung. Trong hình dưới cùng, công việc quan trọng kết xuất sẽ chạy trước, sau đó chuyển đến luồng chính để hiển thị khung mới sớm hơn. Các tác vụ trong nền sẽ chạy sau đó.
Nhấp vào hình trên để 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() ở ví dụ về mã ở trên được thừa nhận là có một chút bí thuật, nhưng đây là một phương pháp hiệu quả hoạt động trong mọi trình duyệt để đảm bảo rằng mã không quan trọng không chặn khung tiếp theo.

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

Bố cục bị đơ (đô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 khi bố cục xảy ra một cách đồng bộ. Điều này xảy ra khi bạn cập nhật các kiểu trong JavaScript và sau đó đọc các kiểu đó trong cùng một tác vụ – và có nhiều thuộc tính trong JavaScript có thể gây ra tình trạng đơ bố cục.

Hình ảnh về tình trạng đơ bố cục như hiển thị trong bảng hiệu suất của Công cụ của Chrome cho nhà phát triển.
Ví dụ về tình trạng đơ bố cục, như minh hoạ trong bảng điều khiển hiệu suất của Công cụ của Chrome cho nhà phát triển. Các tác vụ kết xuất liên quan đến việc đơ bố cục sẽ được ghi chú 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ó nhãn Tính toán lại kiểu hoặc Bố cục.

Bố cục bị nghẽn là nút thắt cổ chai hiệu suất vì bằng cách cập nhật kiểu và yêu cầu ngay lập tức các 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à có thể phải chờ để thực hiện không đồng bộ sau khi lệnh gọi lại sự kiện chạy xong.

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

Độ trễ trình bày của một dấu hiệu tương tác kéo dài từ khi 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 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 một trang nhỏ, công việc hiển thị thường hoàn tất nhanh chóng. Tuy nhiên, khi DOM trở nên rất lớn, hoạt động hiển thị có xu hướng mở rộng quy mô khi kích thước DOM tăng lên. Mối quan hệ giữa công việc hiển thị và kích thước DOM không phải là mối quan hệ tuyến tính, nhưng các DOM lớn lại cần nhiều thao tác hơn để hiển thị so với các DOM nhỏ. Một DOM lớn có vấn đề trong hai trường hợp:

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

Hãy lưu ý rằng có những trường hợp DOM lớn không thể giảm đáng kể. Mặc dù có nhiều phương pháp bạn có thể thực hiện để 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 của bạn nhỏ, những kỹ thuật đó có thể chỉ hiệu quả.

Sử dụng content-visibility để kết xuất từng phần các phần tử ngoài màn hình

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

Lưu ý về chi phí hiệu suất khi hiển thị HTML bằng JavaScript

Ở đâu có HTML, phải có phân tích cú pháp HTML và sau khi trình duyệt hoàn thành phân tích cú pháp HTML thành DOM, trình duyệt phải áp dụng kiểu cho HTML đó, thực hiện các tính toán 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 hiển thị HTML lại là yếu tố quan trọng.

Khi máy chủ gửi HTML, nó sẽ được chuyển đế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ủ đang đến thành các đoạn. Trình duyệt tối ưu hoá cách xử lý luồng bằng cách phân tích cú pháp tăng dần các đoạn của luồng đó khi chúng đến và kết xuất chúng theo từng bit. Đây là một cách tối ưu hoá hiệu suất trong đó trình duyệt ngầm tạo lợi nhuận định kỳ và tự động trong khi tải trang và bạn sẽ nhận được điều đó miễn phí.

Mặc dù lượt truy cập đầu tiên vào bất kỳ trang web nào sẽ luôn liên quan đến một lượng HTML, nhưng cách tiếp cận phổ biến bắt đầu với một lượng HTML ban đầu tối thiểu, sau đó JavaScript được dùng để điền vào vùng nội dung. Các lần cập nhật tiếp theo cho khu vực nội dung đó cũng xảy ra do 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 hạn chế của mẫu này là bằng cách hiển thị HTML bằng JavaScript trên máy khách, bạn không chỉ mất chi phí xử lý JavaScript để tạo HTML đó, mà còn trình duyệt sẽ không lợi nhuận cho đến khi hoàn tất phân tích cú pháp HTML đó và hiển thị nó.

Tuy nhiên, điều quan trọng cần nhớ là ngay cả các trang web không phải SPA cũng có thể liên quan đến một số lượng hiển thị HTML thông qua JavaScript do kết quả của tương tác. Nhìn chung thì việc này vẫn ổn, miễn là bạn không kết xuất một lượng lớn HTML trên ứng dụng khách, điều này có thể trì hoãn việc trình bày khung tiếp theo. Tuy nhiên, điều quan trọng là phải hiểu được ý nghĩa về hiệu suất của phương pháp này trong việc hiển thị HTML trong trình duyệt và tác động của phương pháp này đến khả năng thích ứng của trang web đối với hoạt động đầu vào của người dùng nếu bạn hiển thị nhiều HTML qua 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 bạn khắc phục một lượt tương tác chậm trong trường này, 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). Do đó, bạn cũng cần phải tối ưu hoá các lượt tương tác đó.

Chìa khoá để cải thiện INP chính là tính bền vững. Theo thời gian, bạn có thể nhận được khả năng thích ứng của trang đối với trang mà người dùng hài lòng với trải nghiệm mà bạn mang lại cho họ. Ngoài ra, khi phát triển các tính năng mới cho người dùng, bạn có thể phải thực hiện cùng một quy trình để tối ưu hoá các lượt tương tác dành riêng cho họ. Sẽ mất thời gian và công sức, nhưng bạn sẽ bỏ thời gian và công sức bỏ ra rất hiệu quả.

Hình ảnh chính từ Unsplash của David Pisnoy và được sửa đổi theo giấy phép Unsplash.