Dữ liệu đầu vào đang chuyển đến Trình kết hợp
Đây là phần cuối cùng trong loạt bài viết gồm 4 phần về Chrome; tìm hiểu cách trình duyệt này xử lý mã của chúng ta để hiển thị một trang web. Trong bài đăng trước, chúng ta đã xem xét quá trình kết xuất và tìm hiểu về trình kết hợp. Trong bài đăng này, chúng ta sẽ xem xét cách trình tổng hợp cho phép tương tác mượt mà khi người dùng nhập dữ liệu.
Sự kiện đầu vào theo quan điểm của trình duyệt
Khi nghe thấy cụm từ "sự kiện đầu vào", bạn có thể chỉ nghĩ đến việc nhập vào hộp văn bản hoặc nhấp chuột, nhưng theo quan điểm của trình duyệt, dữ liệu đầu vào có nghĩa là bất kỳ cử chỉ nào của người dùng. Thao tác cuộn bằng con lăn chuột là một sự kiện đầu vào và thao tác chạm hoặc di chuột cũng là một sự kiện đầu vào.
Khi cử chỉ của người dùng như chạm vào màn hình xảy ra, quá trình trình duyệt sẽ là quá trình nhận được cử chỉ đó lúc đầu. Tuy nhiên, quy trình trình duyệt chỉ nhận biết được vị trí xảy ra cử chỉ đó vì nội dung bên trong thẻ được xử lý bằng quy trình kết xuất. Vì vậy, quy trình trình duyệt sẽ gửi loại sự kiện (chẳng hạn như touchstart
) và toạ độ của sự kiện đó đến quy trình trình kết xuất. Quy trình kết xuất xử lý sự kiện một cách thích hợp bằng cách tìm mục tiêu sự kiện và chạy trình nghe sự kiện được đính kèm.
Trình tổng hợp nhận sự kiện đầu vào
Trong bài đăng trước, chúng ta đã xem xét cách trình kết hợp có thể xử lý thao tác cuộn một cách mượt mà bằng cách kết hợp các lớp được quét đường quét. Nếu không có trình nghe sự kiện đầu vào nào được đính kèm vào trang, luồng Compositor có thể tạo một khung tổng hợp mới hoàn toàn độc lập với luồng chính. Nhưng nếu một số trình nghe sự kiện được đính kèm vào trang thì sao? Làm cách nào để luồng trình kết hợp tìm hiểu xem sự kiện có cần được xử lý hay không?
Tìm hiểu về vùng có thể cuộn không nhanh
Vì việc chạy JavaScript là công việc của luồng chính, nên khi một trang được kết hợp, luồng trình kết hợp sẽ đánh dấu một vùng của trang có trình xử lý sự kiện được đính kèm là "Vùng không cuộn nhanh". Khi có thông tin này, luồng trình kết hợp có thể đảm bảo gửi sự kiện đầu vào đến luồng chính nếu sự kiện xảy ra trong vùng đó. Nếu sự kiện đầu vào đến từ bên ngoài khu vực này, thì luồng trình kết hợp sẽ tiếp tục kết hợp khung hình mới mà không cần chờ luồng chính.
Hãy lưu ý khi bạn viết trình xử lý sự kiện
Một mẫu xử lý sự kiện phổ biến trong quá trình phát triển web là uỷ quyền sự kiện. Vì các sự kiện được tạo thành bong bóng, nên bạn có thể đính kèm một trình xử lý sự kiện ở phần tử trên cùng và uỷ quyền các tác vụ dựa trên mục tiêu sự kiện. Bạn có thể đã thấy hoặc viết mã như bên dưới.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Vì bạn chỉ cần viết một trình xử lý sự kiện cho tất cả các phần tử, nên mẫu uỷ quyền sự kiện này rất hấp dẫn. Tuy nhiên, nếu bạn xem mã này từ quan điểm của trình duyệt, thì giờ đây, toàn bộ trang sẽ được đánh dấu là vùng không thể cuộn nhanh. Điều này có nghĩa là ngay cả khi ứng dụng của bạn không quan tâm đến dữ liệu đầu vào từ một số phần nhất định của trang, luồng trình kết hợp vẫn phải giao tiếp với luồng chính và chờ luồng chính mỗi khi có sự kiện đầu vào. Do đó, tính năng cuộn mượt mà của trình tổng hợp bị vô hiệu hoá.
Để giảm thiểu tình trạng này, bạn có thể truyền các tuỳ chọn passive: true
trong trình nghe sự kiện. Điều này cho trình duyệt biết rằng bạn vẫn muốn nghe sự kiện trong luồng chính, nhưng trình tổng hợp cũng có thể tiếp tục và tổng hợp khung mới.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Kiểm tra xem sự kiện có huỷ được hay không
Hãy tưởng tượng bạn có một hộp trong trang mà bạn chỉ muốn giới hạn hướng cuộn ở chế độ cuộn theo chiều ngang.
Việc sử dụng tuỳ chọn passive: true
trong sự kiện con trỏ có nghĩa là thao tác cuộn trang có thể diễn ra mượt mà, nhưng thao tác cuộn theo chiều dọc có thể đã bắt đầu vào thời điểm bạn muốn preventDefault
để giới hạn hướng cuộn. Bạn có thể kiểm tra điều này bằng cách sử dụng phương thức event.cancelable
.
document.body.addEventListener('pointermove', event => {
if (event.cancelable) {
event.preventDefault(); // block the native scroll
/*
* do what you want the application to do here
*/
}
}, {passive: true});
Ngoài ra, bạn có thể sử dụng quy tắc CSS như touch-action
để loại bỏ hoàn toàn trình xử lý sự kiện.
#area {
touch-action: pan-x;
}
Tìm mục tiêu sự kiện
Khi luồng trình kết hợp gửi một sự kiện đầu vào đến luồng chính, việc đầu tiên cần chạy là kiểm thử lượt nhấn để tìm mục tiêu sự kiện. Kiểm thử lượt nhấn sử dụng dữ liệu bản ghi sơn được tạo trong quá trình kết xuất để tìm hiểu nội dung nằm bên dưới toạ độ điểm xảy ra sự kiện.
Giảm thiểu việc gửi sự kiện đến luồng chính
Trong bài đăng trước, chúng ta đã thảo luận về cách màn hình thông thường làm mới màn hình 60 lần/giây và cách chúng ta cần bắt kịp nhịp độ để tạo ảnh động mượt mà. Đối với dữ liệu đầu vào, một thiết bị màn hình cảm ứng thông thường sẽ phân phối sự kiện chạm 60 đến 120 lần mỗi giây và một con chuột thông thường sẽ phân phối sự kiện 100 lần mỗi giây. Sự kiện đầu vào có độ trung thực cao hơn so với khả năng làm mới màn hình.
Nếu một sự kiện liên tục như touchmove
được gửi đến luồng chính 120 lần/giây, thì sự kiện đó có thể kích hoạt quá nhiều lượt kiểm thử lượt nhấn và thực thi JavaScript so với tốc độ làm mới màn hình.
Để giảm thiểu các lệnh gọi không cần thiết đến luồng chính, Chrome hợp nhất các sự kiện liên tục (chẳng hạn như wheel
, mousewheel
, mousemove
, pointermove
, touchmove
) và trì hoãn việc gửi cho đến ngay trước requestAnimationFrame
tiếp theo.
Mọi sự kiện riêng biệt như keydown
, keyup
, mouseup
, mousedown
, touchstart
và touchend
sẽ được gửi ngay lập tức.
Sử dụng getCoalescedEvents
để nhận các sự kiện trong khung
Đối với hầu hết các ứng dụng web, các sự kiện hợp nhất là đủ để mang lại trải nghiệm tốt cho người dùng.
Tuy nhiên, nếu đang xây dựng các ứng dụng như vẽ và đặt đường dẫn dựa trên toạ độ touchmove
, bạn có thể mất toạ độ giữa các toạ độ để vẽ một đường kẻ mượt mà. Trong trường hợp đó, bạn có thể sử dụng phương thức getCoalescedEvents
trong sự kiện con trỏ để lấy thông tin về các sự kiện hợp nhất đó.
window.addEventListener('pointermove', event => {
const events = event.getCoalescedEvents();
for (let event of events) {
const x = event.pageX;
const y = event.pageY;
// draw a line using x and y coordinates.
}
});
Các bước tiếp theo
Trong loạt bài này, chúng ta đã đề cập đến cách hoạt động bên trong của trình duyệt web. Nếu bạn chưa bao giờ nghĩ về lý do DevTools đề xuất thêm {passive: true}
vào trình xử lý sự kiện hoặc lý do bạn có thể ghi thuộc tính async
trong thẻ tập lệnh, tôi hy vọng loạt bài này sẽ làm sáng tỏ lý do trình duyệt cần những thông tin đó để mang lại trải nghiệm web nhanh và mượt mà hơn.
Sử dụng Lighthouse
Nếu bạn muốn mã của mình phù hợp với trình duyệt nhưng không biết bắt đầu từ đâu, thì Lighthouse là một công cụ chạy quy trình kiểm tra mọi trang web và cung cấp cho bạn báo cáo về những gì đang được thực hiện đúng cách và những gì cần cải thiện. Việc đọc qua danh sách kiểm tra cũng giúp bạn biết được những điều mà trình duyệt quan tâm.
Tìm hiểu cách đo lường hiệu suất
Các biện pháp điều chỉnh hiệu suất có thể khác nhau tuỳ theo trang web. Vì vậy, điều quan trọng là bạn phải đo lường hiệu suất của trang web và quyết định biện pháp nào phù hợp nhất với trang web của mình. Nhóm Công cụ của Chrome cho nhà phát triển có một số hướng dẫn về cách đo lường hiệu suất của trang web.
Thêm Chính sách về tính năng vào trang web của bạn
Nếu bạn muốn thực hiện thêm một bước, Chính sách về tính năng là một tính năng mới của nền tảng web có thể là một hàng rào bảo vệ cho bạn khi bạn xây dựng dự án. Việc bật chính sách tính năng sẽ đảm bảo một số hành vi nhất định của ứng dụng và giúp bạn tránh mắc lỗi.
Ví dụ: Nếu muốn đảm bảo ứng dụng của mình không bao giờ chặn quá trình phân tích cú pháp, bạn có thể chạy ứng dụng theo chính sách tập lệnh đồng bộ. Khi sync-script: 'none'
được bật, JavaScript chặn trình phân tích cú pháp sẽ không được thực thi. Điều này giúp ngăn mọi mã của bạn chặn trình phân tích cú pháp và trình duyệt không cần lo lắng về việc tạm dừng trình phân tích cú pháp.
Tóm tắt
Khi bắt đầu xây dựng trang web, tôi hầu như chỉ quan tâm đến cách viết mã và những gì giúp tôi làm việc hiệu quả hơn. Những điều đó rất quan trọng, nhưng chúng ta cũng nên nghĩ về cách trình duyệt lấy mã chúng ta viết. Các trình duyệt hiện đại đã và đang tiếp tục đầu tư vào việc mang lại trải nghiệm web tốt hơn cho người dùng. Việc tạo điều kiện cho trình duyệt bằng cách sắp xếp mã sẽ giúp cải thiện trải nghiệm người dùng. Tôi hy vọng bạn sẽ cùng tôi thực hiện hành trình làm hài lòng các trình duyệt!
Cảm ơn tất cả những người đã xem xét các bản nháp ban đầu của loạt bài viết này, bao gồm (nhưng không giới hạn ở): Alex Russell, Paul Irish, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasuda, Nasko Oskov và Charlie Reis.
Bạn có thích loạt bài viết này không? Nếu bạn có câu hỏi hoặc đề xuất cho bài đăng trong tương lai, hãy chia sẻ với tôi trong phần bình luận bên dưới hoặc trên Twitter theo tên người dùng @kosamari.