Tối ưu hoá độ trễ khi nhập

Tìm hiểu độ trễ đầu vào là gì và tìm hiểu các kỹ thuật để giảm độ trễ đầu vào nhằm tăng khả năng tương tác.

Hoạt động tương tác trên web là một vấn đề phức tạp, với tất cả các loại hoạt động diễn ra trong trình duyệt để thúc đẩy hoạt động tương tác. Tuy nhiên, điểm chung của tất cả các phương thức này là chúng sẽ bị trễ một chút về đầu vào trước khi lệnh gọi lại sự kiện bắt đầu chạy. Trong hướng dẫn này, bạn sẽ tìm hiểu độ trễ đầu vào là gì và những việc bạn có thể làm để giảm thiểu độ trễ này nhằm giúp các hoạt động tương tác trên trang web diễn ra nhanh hơn.

Độ trễ khi nhập là gì?

Thời gian phản hồi đầu vào là khoảng thời gian bắt đầu từ khi người dùng tương tác lần đầu với một trang (chẳng hạn như nhấn vào màn hình, nhấp bằng chuột hoặc nhấn phím) cho đến khi lệnh gọi lại sự kiện cho lượt tương tác đó bắt đầu chạy. Mỗi lượt tương tác đều bắt đầu bằng một độ trễ đầu vào nhất định.

Hình ảnh trực quan đơn giản về độ trễ khi nhập. Ở bên trái, có hình minh hoạ đường viền của con trỏ chuột với một hình ngôi sao phía sau, biểu thị sự bắt đầu của một lượt tương tác. Ở bên phải là hình minh hoạ đường viền của một bánh răng, cho biết thời điểm trình xử lý sự kiện cho một lượt tương tác bắt đầu chạy. Khoảng trống ở giữa được ghi nhận là độ trễ đầu vào bằng dấu ngoặc nhọn.
Cơ chế đằng sau độ trễ khi nhập. Khi hệ điều hành nhận được dữ liệu đầu vào, dữ liệu đó phải được chuyển đến trình duyệt trước khi quá trình tương tác bắt đầu. Quá trình này mất một khoảng thời gian nhất định và có thể tăng lên do công việc hiện có trên luồng chính.

Một số phần của độ trễ đầu vào là không thể tránh khỏi: hệ điều hành luôn mất một khoảng thời gian để nhận dạng một sự kiện đầu vào và chuyển sự kiện đó đến trình duyệt. Tuy nhiên, phần độ trễ đầu vào đó thường không đáng kể và có những điều khác xảy ra trên chính trang có thể khiến độ trễ đầu vào đủ lâu để gây ra vấn đề.

Cách suy nghĩ về độ trễ đầu vào

Nhìn chung, bạn nên rút ngắn mọi phần của một lượt tương tác càng nhiều càng tốt để trang web của bạn có nhiều cơ hội nhất đáp ứng ngưỡng "tốt" của chỉ số Lượt tương tác đến nội dung hiển thị tiếp theo (INP), bất kể thiết bị của người dùng. Việc kiểm soát độ trễ đầu vào chỉ là một phần của việc đáp ứng ngưỡng đó.

Do đó, bạn nên hướng đến độ trễ đầu vào ngắn nhất có thể để đáp ứng ngưỡng "tốt" của INP. Tuy nhiên, bạn cần lưu ý rằng không thể loại bỏ hoàn toàn độ trễ đầu vào. Miễn là bạn tránh làm quá nhiều việc trên luồng chính trong khi người dùng đang cố gắng tương tác với trang của bạn, độ trễ đầu vào phải đủ thấp để tránh sự cố.

Cách giảm thiểu độ trễ khi nhập

Như đã nói trước đó, một số độ trễ đầu vào là không thể tránh khỏi, nhưng mặt khác, một số độ trễ đầu vào có thể tránh được. Dưới đây là một số điều cần cân nhắc nếu bạn đang gặp phải độ trễ đầu vào lâu.

Tránh các bộ hẹn giờ định kỳ kích hoạt quá nhiều công việc trên luồng chính

Có hai hàm hẹn giờ thường dùng trong JavaScript có thể góp phần gây ra độ trễ đầu vào: setTimeoutsetInterval. Sự khác biệt giữa hai phương thức này là setTimeout lên lịch lệnh gọi lại để chạy sau một khoảng thời gian cụ thể. Mặt khác, setInterval lên lịch gọi lại để chạy mỗi n mili giây vĩnh viễn hoặc cho đến khi dừng bộ hẹn giờ bằng clearInterval.

setTimeout không tự nó gây ra vấn đề – thực tế, nó có thể hữu ích trong việc tránh các tác vụ dài. Tuy nhiên, điều này còn tuỳ thuộc vào thời điểm hết thời gian chờ và liệu người dùng có cố gắng tương tác với trang khi lệnh gọi lại hết thời gian chờ chạy hay không.

Ngoài ra, bạn có thể chạy setTimeout trong một vòng lặp hoặc đệ quy, trong đó setTimeout hoạt động giống như setInterval, mặc dù tốt nhất là không lên lịch lặp lại tiếp theo cho đến khi lặp lại trước đó hoàn tất. Mặc dù điều này có nghĩa là vòng lặp sẽ trả về luồng chính mỗi khi setTimeout được gọi, nhưng bạn nên cẩn thận để đảm bảo lệnh gọi lại của vòng lặp không làm quá nhiều việc.

setInterval chạy lệnh gọi lại theo một khoảng thời gian, do đó, nhiều khả năng sẽ cản trở các hoạt động tương tác. Lý do là – không giống như một thực thể duy nhất của lệnh gọi setTimeout, đây là lệnh gọi lại một lần có thể cản trở hoạt động tương tác của người dùng – bản chất định kỳ của setInterval khiến khả năng sẽ cản trở hoạt động tương tác cao hơn nhiều, do đó làm tăng độ trễ đầu vào của hoạt động tương tác.

Ảnh chụp màn hình của trình phân tích hiệu suất trong Chrome DevTools minh hoạ độ trễ đầu vào. Một tác vụ do hàm hẹn giờ kích hoạt sẽ xảy ra ngay trước khi người dùng bắt đầu tương tác bằng lượt nhấp. Tuy nhiên, bộ hẹn giờ sẽ kéo dài độ trễ đầu vào, khiến lệnh gọi lại sự kiện của lượt tương tác chạy muộn hơn so với bình thường.
Một bộ hẹn giờ được đăng ký bằng lệnh gọi setInterval trước đó góp phần gây ra độ trễ đầu vào như mô tả trong bảng điều khiển hiệu suất của Chrome DevTools. Độ trễ đầu vào được thêm vào khiến lệnh gọi lại sự kiện cho lượt tương tác chạy muộn hơn so với bình thường.

Nếu bộ hẹn giờ đang diễn ra trong mã của bên thứ nhất, thì bạn có quyền kiểm soát các bộ hẹn giờ đó. Hãy đánh giá xem bạn có cần các lớp này hay không hoặc cố gắng giảm thiểu công việc trong các lớp này nhiều nhất có thể. Tuy nhiên, bộ hẹn giờ trong tập lệnh của bên thứ ba lại là một chuyện khác. Bạn thường không kiểm soát được hoạt động của tập lệnh bên thứ ba và việc khắc phục các vấn đề về hiệu suất trong mã bên thứ ba thường liên quan đến việc làm việc với các bên liên quan để xác định xem có cần một tập lệnh bên thứ ba cụ thể hay không. Nếu có, hãy liên hệ với nhà cung cấp tập lệnh bên thứ ba để xác định những việc có thể làm để khắc phục các vấn đề về hiệu suất mà các tập lệnh đó có thể gây ra trên trang web của bạn.

Tránh các tác vụ dài

Một cách để giảm thiểu độ trễ đầu vào lâu là tránh các tác vụ dài. Khi bạn có quá nhiều công việc trên luồng chính chặn luồng chính trong quá trình tương tác, điều đó sẽ làm tăng độ trễ đầu vào cho các luồng đó trước khi các tác vụ dài có cơ hội hoàn tất.

Hình ảnh trực quan về khoảng thời gian các tác vụ kéo dài độ trễ đầu vào. Ở trên cùng, một lượt tương tác xảy ra ngay sau khi một tác vụ dài chạy, gây ra độ trễ đầu vào đáng kể khiến lệnh gọi lại sự kiện chạy muộn hơn nhiều so với dự kiến. Ở dưới cùng, một lượt tương tác xảy ra gần như cùng một lúc, nhưng tác vụ dài được chia thành một số tác vụ nhỏ hơn bằng cách nhường quyền, cho phép lệnh gọi lại sự kiện của lượt tương tác chạy sớm hơn nhiều.
Hình ảnh minh hoạ những gì xảy ra với các lượt tương tác khi tác vụ quá dài và trình duyệt không thể phản hồi đủ nhanh với các lượt tương tác, so với khi các tác vụ dài hơn được chia thành các tác vụ nhỏ hơn.

Ngoài việc giảm thiểu lượng công việc bạn thực hiện trong một tác vụ (và bạn luôn phải cố gắng làm ít việc nhất có thể trên luồng chính), bạn có thể cải thiện khả năng phản hồi đối với hoạt động đầu vào của người dùng bằng cách chia nhỏ các tác vụ dài.

Chú ý đến việc chồng chéo lượt tương tác

Một phần đặc biệt khó khăn trong việc tối ưu hoá INP có thể là khi bạn có các lượt tương tác trùng lặp. Sự chồng chéo tương tác có nghĩa là sau khi tương tác với một phần tử, bạn tương tác với trang khác trước khi lượt tương tác ban đầu có cơ hội hiển thị khung hình tiếp theo.

Hình ảnh mô tả thời điểm các tác vụ có thể chồng chéo nhau để tạo ra độ trễ đầu vào dài. Trong hình minh hoạ này, một lượt tương tác nhấp chồng lên một lượt tương tác nhấn phím để tăng độ trễ đầu vào cho lượt tương tác nhấn phím.
Hình ảnh trực quan của hai lượt tương tác đồng thời trong trình phân tích hiệu suất trong DevTools của Chrome. Công việc kết xuất trong lượt tương tác nhấp ban đầu gây ra độ trễ đầu vào cho lượt tương tác bằng bàn phím tiếp theo.

Nguồn gốc của sự trùng lặp lượt tương tác có thể đơn giản như người dùng thực hiện nhiều lượt tương tác trong một khoảng thời gian ngắn. Điều này có thể xảy ra khi người dùng nhập vào các trường biểu mẫu, nơi có thể có nhiều lượt tương tác bằng bàn phím trong một khoảng thời gian rất ngắn. Nếu công việc trên một sự kiện nhấn phím đặc biệt tốn kém (chẳng hạn như trong trường hợp phổ biến của các trường tự động hoàn thành, trong đó các yêu cầu mạng được thực hiện đến phần phụ trợ), bạn có một số lựa chọn:

  • Hãy cân nhắc việc giảm độ trễ đầu vào để giới hạn số lần lệnh gọi lại sự kiện thực thi trong một khoảng thời gian nhất định.
  • Sử dụng AbortController để huỷ các yêu cầu fetch đi để luồng chính không bị tắc nghẽn khi xử lý lệnh gọi lại fetch. Lưu ý: bạn cũng có thể sử dụng thuộc tính signal của thực thể AbortController để huỷ sự kiện.

Một nguồn khác gây ra độ trễ đầu vào do các lượt tương tác chồng chéo có thể là ảnh động tốn kém. Cụ thể, ảnh động trong JavaScript có thể kích hoạt nhiều lệnh gọi requestAnimationFrame, điều này có thể cản trở hoạt động tương tác của người dùng. Để khắc phục vấn đề này, hãy sử dụng ảnh động CSS bất cứ khi nào có thể để tránh xếp hàng các khung ảnh động có thể tốn kém. Tuy nhiên, nếu bạn làm như vậy, hãy nhớ tránh sử dụng ảnh động không kết hợp để ảnh động chủ yếu chạy trên GPU và luồng trình kết hợp, chứ không phải trên luồng chính.

Kết luận

Mặc dù độ trễ đầu vào có thể không chiếm phần lớn thời gian tương tác, nhưng điều quan trọng là bạn phải hiểu rằng mọi phần của một lượt tương tác đều chiếm một khoảng thời gian mà bạn có thể giảm thiểu. Nếu nhận thấy độ trễ đầu vào lâu, thì bạn có cơ hội giảm độ trễ đó. Việc tránh các lệnh gọi lại định kỳ của bộ hẹn giờ, chia nhỏ các tác vụ dài và nhận biết khả năng tương tác chồng chéo đều có thể giúp bạn giảm độ trễ đầu vào, từ đó tăng khả năng tương tác nhanh hơn cho người dùng trang web.

Hình ảnh chính trên Unsplash, của Erik Mclean.