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á lượt 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 phản hồi 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ả 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 được ghi nhận (đô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 gian hiển thị nội dung tiếp theo trong khoảng 200 mili giây trở xuống. Để đảm bảo hầu hết người dùng của bạn đạt được mục tiêu này, ngưỡng phù hợp để đo lường là tỷ lệ phần trăm thứ 75 của số lượt tải trang, được phân đoạn trên thiết bị di động và thiết bị máy tính.

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à mọi thứ ở giữa đều cần cải thiện.

Tuỳ thuộc vào trang web, có thể có ít hoặc không có 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ủa 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. Trong cả hai trường hợp, khi có INP cao, trải nghiệm người dùng sẽ gặp rủi ro.

Cần thời gian và công sức để cải thiện INP 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, sẽ khám phá một lộ trình để cải thiện INP.

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

Để có thể khắc phục tình trạng 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 hay cần cải thiện hay không. Sau khi có thông tin đó, bạn có thể chuyển đến phòng thí nghiệm để bắt đầu chẩn đoán các hiện tượng tương tác chậm và tìm cách tìm ra giải pháp.

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

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

Chẩn đoán 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 tương tác chậm. Khi không có dữ liệu tại hiện trường, có một số chiến lược để xác định các tương tác chậm trong phòng thí nghiệm. Các chiến lược đó bao gồm các luồng người dùng phổ biến và thử nghiệm các tương tác trong quá trình tải, cũng như tương tác với trang trong quá trình 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 được một tương tác chậm và có thể tái tạo 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 đó. Các hoạt động tương tác có thể được chia thành ba giai đoạn:

  1. Độ trễ đầu vào, bắt đầu khi người dùng bắt đầu một lượt tương tác với trang và kết thúc khi lệnh gọi lại sự kiện cho lượt tương tác bắt đầu chạy.
  2. Thời lượng 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ị, là thời gian để trình duyệt hiển thị khung hình tiếp theo chứa kết quả hình ảnh 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 một của 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 tương tác để phần tương tác chạy trong ít thời gian 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ể kéo dài đáng kể. Điều này có thể là do hoạt động diễn ra trên luồng chính (có thể là do 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 diễn ra liên tiếp và chồng chéo với nhau.

Bất kể nguyên nhân gây ra độ trễ đầu vào của lượt tương tác là gì, bạn đều 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ụ 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 giai đoạn khởi động. Khi một trang được tải, ban đầu trang đó sẽ hiển thị, nhưng xin lưu ý rằng việc một trang đã được 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 để có đầ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 là một việc có thể làm kéo dài độ trễ đầu vào của một lượt tương tác khi tải trang. Sau khi tìm nạp tệp JavaScript từ mạng, trình duyệt vẫn còn 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 một tập lệnh để đảm bảo rằng cú pháp của tệp là hợp lệ, biên dịch tệp thành mã byte và cuối cùng là thực thi tệp đó.

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

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

Độ trễ đầu vào chỉ là phần đầu tiên trong phép đo 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 nhường lời cho luồng chính

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

Nếu bạn thấy đây là trường hợp của trang web của mình, điều tiếp theo bạn có thể thử là chia nhỏ công việc trong các 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 công việc chung không trở thành một nhiệm vụ mất nhiều thời gian chặn luồng chính, từ đó cho phép các hoạt động tương tác khác vốn phải chờ 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 vào sẽ chạy trong một tác vụ mới. Bạn có thể tự sử dụng setTimeout hoặc trừu tượng sử dụng thành một hàm riêng biệt để mang lại hiệu quả cao hơn.

Tạo ra bừa bãi tốt hơn là không tạo ra kết quả nào. Tuy nhiên, có một cách tinh tế hơn để tạo ra luồng chính và chỉ tạo ra kết quả ngay sau khi 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

Một kỹ thuật tạo lợi nhuận nâng cao hơn bao gồm việc cấu trúc mã trong lệnh gọi lại sự kiện để giới hạn những gì được chạy chỉ cho logic cần thiết để áp dụng nội dung cập nhật hình ảnh cho khung tiếp theo. Mọi thứ khác có thể được trì hoãn cho tác vụ tiếp theo. Điều này không chỉ giúp các lệnh gọi lại nhẹ nhàng và linh hoạt, mà còn cải thiện thời gian kết xuất cho các hoạt động 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 cũng cập nhật các khía cạnh khác của giao diện người dùng theo nội dung bạn đã viết (chẳng hạn như số từ, đánh dấu 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 phải lưu lại những gì bạn đã viết để nếu bạn rời đi và quay lại thì không bị mất bất kỳ công việc nào.

Trong ví dụ này, 4 điều sau cần xảy ra để phản hồi các ký tự do người dùng nhập. Tuy nhiên, bạn chỉ cần hoàn thành mục đầu tiên trước khi khung hình tiếp theo sẽ 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 định dạng bắt buộc.
  2. Cập nhật phần giao diện người dùng cho thấy 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 máy hoặc vào cơ sở dữ liệu từ xa).

Mã để thực hiện việc này có thể trông 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 sau khung hình tiếp theo có thể làm giảm thời gian xử lý và từ đó giảm độ trễ tổng thể của lượt tương tác như thế nào.

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

Tình trạng đơ bố cục (đôi khi được gọi là bố cục đồng bộ bắt buộc) là vấn đề về hiệu suất kết xuất trong đó bố cục xảy ra một cách đồng bộ. Lỗi này xảy ra khi bạn cập nhật các kiểu trong JavaScript rồi đọ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ư trong bảng điều khiển hiệu suất của Công cụ cho nhà phát triển của Chrome.
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ụ cho nhà phát triển của Chrome. Các tác vụ hiển thị liên quan đến tình trạng đơ 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 gắn nhãn Tính toán lại kiểu hoặc Bố cục.

Tình trạng đơ bố cục là điểm tắc nghẽn hiệu suất vì bằng cách cập nhật các 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ộ, nếu không thì có thể phải đợi để thực hiện không đồng bộ 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 dấu tương tác kéo dài từ khi lệnh gọi lại sự kiện của một tương tác chạy xong, cho tới thời điểm trình duyệt có thể vẽ khung tiếp theo cho thấy những thay đổi hình ảnh kết quả.

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 kết thúc 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 mở rộng khi kích thước DOM tăng lên. 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 yêu cầu nhiều công việc để kết xuất hơn các DOM nhỏ. DOM lớn có vấn đề trong 2 trường hợp:

  1. Trong lần hiển thị trang ban đầu, nơi DOM lớn yêu cầu nhiều công sứ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, trong đó DOM lớn có thể khiến việc cập nhật kết xuất rất tốn kém và do đó làm tăng thời gian trình duyệt hiển thị khung hình tiếp theo.

Hãy lưu ý rằng có những trường hợp không thể giảm đáng kể các DOM lớn. 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ảm kích thước DOM ban đầu, nhưng 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 thành phần ngoài màn hình

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

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

Ở đâu có HTML thì có phân tích cú pháp HTML và sau khi trình duyệt hoàn tất 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 phép tính bố cục và sau đó hiển thị bố cục đó. Đây là chi phí không thể tránh khỏi, nhưng cách bạn thực hiện việc hiển thị HTML rất quan trọng.

Khi máy chủ gửi HTML, HTML đó sẽ xuất hiện trong 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 các phân đ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à hiển thị chúng từng chút một. Đây là một phương pháp tối ưu hoá hiệu suất, trong đó trình duyệt ngầm tạo ra định kỳ và tự động trong quá trình tải trang, và bạn sẽ nhận được lợi ích đó miễn phí.

Mặc dù lượt truy cập đầu tiên vào trang web bất kỳ sẽ luôn bao gồm một số lượng HTML, nhưng cách tiếp cận phổ biến bắt đầu với một chút HTML ban đầu tối thiểu, sau đó JavaScript được sử dụng để điền sẵn vùng nội dung. Các lần cập nhật tiếp theo đối với vùng nội dung đó cũng xảy ra do người dùng tương tác. Mô hình này thường được gọi là mô hình ứng dụng một trang (SPA). Một hạn chế của mẫu này là, bằng cách hiển thị HTML với JavaScript trên máy khách, bạn không chỉ phải trả chi phí xử lý JavaScript để tạo HTML đó mà cả trình duyệt sẽ không tạo ra 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ả những trang web không phải là SPA cũng có thể liên quan đến một số hoạt động hiển thị HTML thông qua JavaScript do các hoạt động tương tác. Nói chung là bình thường, miễn là bạn không kết xuất một lượng lớn HTML trên máy khách, điều này có thể làm chậm việc trình bày khung tiếp theo. Tuy nhiên, điều quan trọng là phải hiểu được hệ quả về hiệu suất của phương pháp kết xuất HTML trong trình duyệt này và cách nó có thể tác độ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 qua JavaScript.

Kết luận

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

Chìa khoá để cải thiện INP là tính bền vững. Theo thời gian, bạn có thể khiến trang của bạn phản hồi nhanh chóng ở những vị trí mà người dùng hài lòng với trải nghiệm bạn đang mang lại cho họ. Khả năng cao là khi bạn phát triển các tính năng mới cho người dùng của mình, bạn có thể cần phải thực hiện cùng một quy trình để tối ưu hóa các tương tác dành riêng cho họ. Sẽ tốn thời gian và công sức, nhưng bạn sẽ phải đầu tư thời gian và công sức một cách xứng đáng.

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