Tampilan mendalam pada browser web modern (bagian 4)

Mariko Kosaka

Input masuk ke Compositor

Ini adalah bagian terakhir dari seri blog 4 bagian yang melihat ke dalam Chrome; menyelidiki cara Chrome menangani kode kita untuk menampilkan situs. Dalam postingan sebelumnya, kita telah melihat proses rendering dan mempelajari kompositor. Dalam postingan ini, kita akan melihat cara compositor memungkinkan interaksi yang lancar saat input pengguna masuk.

Peristiwa input dari sudut pandang browser

Saat mendengar "peristiwa input", Anda mungkin hanya memikirkan pengetikan di kotak teks atau klik mouse, tetapi dari sudut pandang browser, input berarti gestur apa pun dari pengguna. Scroll roda mouse adalah peristiwa input dan sentuh atau mouse over juga merupakan peristiwa input.

Saat gestur pengguna seperti sentuhan pada layar terjadi, proses browser adalah yang menerima gestur pada awalnya. Namun, proses browser hanya mengetahui tempat gestur tersebut terjadi karena konten di dalam tab ditangani oleh proses perender. Jadi, proses browser mengirimkan jenis peristiwa (seperti touchstart) dan koordinatnya ke proses perender. Proses perender menangani peristiwa dengan tepat dengan menemukan target peristiwa dan menjalankan pemroses peristiwa yang terpasang.

peristiwa input
Gambar 1: Peristiwa input yang dirutekan melalui proses browser ke proses perender

Compositor menerima peristiwa input

Gambar 2: Viewport yang melayang di atas lapisan halaman

Di postingan sebelumnya, kita telah melihat cara kompositor dapat menangani scroll dengan lancar dengan menggabungkan lapisan raster. Jika tidak ada pemroses peristiwa input yang dilampirkan ke halaman, thread Compositor dapat membuat frame komposit baru yang sepenuhnya independen dari thread utama. Namun, bagaimana jika beberapa pemroses peristiwa disertakan ke halaman? Bagaimana thread kompositor mengetahui apakah peristiwa perlu ditangani?

Memahami region yang tidak dapat di-scroll dengan cepat

Karena menjalankan JavaScript adalah tugas thread utama, saat halaman dikomposisi, thread kompositor akan menandai area halaman yang memiliki pengendali peristiwa yang dilampirkan sebagai "Area yang Tidak Dapat Di-scroll dengan Cepat". Dengan memiliki informasi ini, thread kompositor dapat memastikan untuk mengirim peristiwa input ke thread utama jika peristiwa terjadi di wilayah tersebut. Jika peristiwa input berasal dari luar region ini, thread komponer akan terus menyusun frame baru tanpa menunggu thread utama.

area yang tidak dapat di-scroll dengan cepat
Gambar 3: Diagram input yang dijelaskan ke wilayah yang tidak dapat di-scroll dengan cepat

Perhatikan saat Anda menulis pengendali peristiwa

Pola penanganan peristiwa yang umum dalam pengembangan web adalah delegasi peristiwa. Karena peristiwa berbentuk balon, Anda dapat melampirkan satu pengendali peristiwa di elemen paling atas dan mendelegasikan tugas berdasarkan target peristiwa. Anda mungkin pernah melihat atau menulis kode seperti di bawah.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault();
    }
});

Karena Anda hanya perlu menulis satu pengendali peristiwa untuk semua elemen, ergonomi pola delegasi peristiwa ini menarik. Namun, jika Anda melihat kode ini dari sudut pandang browser, sekarang seluruh halaman ditandai sebagai area yang tidak dapat di-scroll dengan cepat. Artinya, meskipun aplikasi Anda tidak peduli dengan input dari bagian halaman tertentu, thread kompositor harus berkomunikasi dengan thread utama dan menunggunya setiap kali peristiwa input masuk. Dengan demikian, kemampuan scroll yang lancar dari compositor akan dihentikan.

wilayah halaman penuh yang tidak dapat di-scroll dengan cepat
Gambar 4: Diagram input yang dijelaskan ke wilayah yang tidak dapat di-scroll dengan cepat yang mencakup seluruh halaman

Untuk mengurangi hal ini, Anda dapat meneruskan opsi passive: true di pemroses peristiwa. Hal ini mengisyaratkan kepada browser bahwa Anda masih ingin memproses peristiwa di thread utama, tetapi kompositor juga dapat melanjutkan dan menyusun frame baru.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

Memeriksa apakah peristiwa dapat dibatalkan

scroll halaman
Gambar 5: Halaman web dengan bagian halaman yang disetel ke scroll horizontal

Bayangkan Anda memiliki kotak di halaman yang ingin Anda batasi arah scroll-nya hanya ke scroll horizontal.

Menggunakan opsi passive: true dalam peristiwa pointer berarti scroll halaman dapat berjalan lancar, tetapi scroll vertikal mungkin telah dimulai pada saat Anda ingin preventDefault untuk membatasi arah scroll. Anda dapat memeriksanya menggunakan metode 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});

Atau, Anda dapat menggunakan aturan CSS seperti touch-action untuk sepenuhnya menghapus pengendali peristiwa.

#area {
  touch-action: pan-x;
}

Menemukan target peristiwa

hit test
Gambar 6: Thread utama melihat data cat yang menanyakan apa yang digambar pada titik x.y

Saat thread kompositor mengirim peristiwa input ke thread utama, hal pertama yang harus dijalankan adalah hit test untuk menemukan target peristiwa. Pengujian hit menggunakan data catatan cat yang dihasilkan dalam proses rendering untuk mengetahui apa yang ada di bawah koordinat titik tempat peristiwa terjadi.

Meminimalkan pengiriman peristiwa ke thread utama

Di postingan sebelumnya, kita telah membahas cara layar standar memuat ulang layar 60 kali per detik dan cara kita harus mengikuti irama untuk animasi yang lancar. Untuk input, perangkat layar sentuh biasa mengirimkan peristiwa sentuh 60-120 kali per detik, dan mouse biasa mengirimkan peristiwa 100 kali per detik. Peristiwa input memiliki fidelitas yang lebih tinggi daripada kemampuan layar untuk memuat ulang.

Jika peristiwa berkelanjutan seperti touchmove dikirim ke thread utama 120 kali per detik, hal ini dapat memicu jumlah hit test dan eksekusi JavaScript yang berlebihan dibandingkan dengan seberapa lambat layar dapat dimuat ulang.

peristiwa yang tidak difilter
Gambar 7: Peristiwa yang membanjiri linimasa frame menyebabkan jank halaman

Untuk meminimalkan panggilan yang berlebihan ke thread utama, Chrome menggabungkan peristiwa berkelanjutan (seperti wheel, mousewheel, mousemove, pointermove, touchmove ) dan menunda pengiriman hingga tepat sebelum requestAnimationFrame berikutnya.

peristiwa gabungan
Gambar 8: Linimasa yang sama seperti sebelumnya, tetapi peristiwa digabungkan dan tertunda

Setiap peristiwa terpisah seperti keydown, keyup, mouseup, mousedown, touchstart, dan touchend akan segera dikirim.

Menggunakan getCoalescedEvents untuk mendapatkan peristiwa intra-frame

Untuk sebagian besar aplikasi web, peristiwa gabungan seharusnya cukup untuk memberikan pengalaman pengguna yang baik. Namun, jika Anda mem-build hal-hal seperti aplikasi gambar dan menempatkan jalur berdasarkan koordinat touchmove, Anda mungkin kehilangan koordinat di antara untuk menggambar garis yang halus. Dalam hal ini, Anda dapat menggunakan metode getCoalescedEvents dalam peristiwa pointer untuk mendapatkan informasi tentang peristiwa gabungan tersebut.

getCoalescedEvents
Gambar 9: Jalur gestur sentuh yang halus di sebelah kiri, jalur terbatas yang digabungkan di sebelah kanan
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.
    }
});

Langkah berikutnya

Dalam seri ini, kita telah membahas cara kerja browser web. Jika Anda belum pernah memikirkan mengapa DevTools merekomendasikan penambahan {passive: true} pada pengendali peristiwa atau mengapa Anda mungkin menulis atribut async di tag skrip, saya harap seri ini dapat menjelaskan mengapa browser memerlukan informasi tersebut untuk memberikan pengalaman web yang lebih cepat dan lancar.

Menggunakan Lighthouse

Jika Anda ingin membuat kode yang baik untuk browser, tetapi tidak tahu harus memulai dari mana, Lighthouse adalah alat yang menjalankan audit situs apa pun dan memberi Anda laporan tentang apa yang dilakukan dengan benar dan apa yang perlu ditingkatkan. Membaca daftar audit juga memberi Anda gambaran tentang hal-hal yang penting bagi browser.

Pelajari cara mengukur performa

Penyesuaian performa dapat bervariasi untuk situs yang berbeda, jadi Anda harus mengukur performa situs dan memutuskan penyesuaian yang paling sesuai untuk situs Anda. Tim Chrome DevTools memiliki beberapa tutorial tentang cara mengukur performa situs Anda.

Menambahkan Kebijakan Fitur ke situs Anda

Jika Anda ingin melakukan langkah tambahan, Kebijakan Fitur adalah fitur platform web baru yang dapat menjadi penjaga bagi Anda saat mem-build project. Mengaktifkan kebijakan fitur menjamin perilaku tertentu aplikasi Anda dan mencegah Anda melakukan kesalahan. Misalnya, jika ingin memastikan aplikasi tidak akan pernah memblokir penguraian, Anda dapat menjalankan aplikasi pada kebijakan skrip sinkron. Jika sync-script: 'none' diaktifkan, JavaScript pemblokir parser akan dicegah agar tidak dieksekusi. Hal ini mencegah kode Anda memblokir parser, dan browser tidak perlu khawatir untuk menjeda parser.

Rangkuman

terima kasih

Saat mulai membuat situs, saya hampir hanya peduli dengan cara menulis kode dan hal-hal yang akan membantu saya menjadi lebih produktif. Hal-hal tersebut penting, tetapi kita juga harus memikirkan cara browser mengambil kode yang kita tulis. Browser modern telah dan terus berinvestasi dalam cara untuk memberikan pengalaman web yang lebih baik bagi pengguna. Dengan memperlakukan browser dengan baik dengan mengatur kode, pengalaman pengguna Anda akan meningkat. Semoga Anda bergabung dengan saya dalam misi untuk bersikap baik kepada browser.

Terima kasih banyak kepada semua orang yang meninjau draf awal seri ini, termasuk (tetapi tidak terbatas pada): Alex Russell, Paul Irish, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasuda, Nasko Oskov, dan Charlie Reis.

Apakah Anda menikmati seri ini? Jika ada pertanyaan atau saran untuk postingan mendatang, kami ingin mendengar pendapat Anda di bagian komentar di bawah atau @kosamari di Twitter.