Cara kerja internal Proses Perender
Ini adalah bagian 3 dari 4 bagian seri blog yang membahas cara kerja browser. Sebelumnya, kita telah membahas arsitektur multiproses dan alur navigasi. Dalam postingan ini, kita akan melihat apa yang terjadi di dalam proses perender.
Proses perender menyentuh banyak aspek performa web. Karena ada banyak hal yang terjadi di dalam proses perender, postingan ini hanyalah ringkasan umum. Jika Anda ingin mengetahui lebih dalam, bagian Performa di Dasar-Dasar Web memiliki banyak referensi lainnya.
Proses perender menangani konten web
Proses perender bertanggung jawab atas semua yang terjadi di dalam tab. Dalam proses perender, thread utama menangani sebagian besar kode yang Anda kirim ke pengguna. Terkadang bagian JavaScript ditangani oleh thread pekerja jika Anda menggunakan pekerja web atau pekerja layanan. Thread kompositor dan raster juga dijalankan di dalam proses perender untuk merender halaman secara efisien dan lancar.
Tugas inti proses perender adalah mengubah HTML, CSS, dan JavaScript menjadi halaman web yang dapat berinteraksi dengan pengguna.
Penguraian
Konstruksi DOM
Saat proses perender menerima pesan commit untuk navigasi dan mulai menerima data HTML, thread utama akan mulai mengurai string teks (HTML) dan mengubahnya menjadi Document Object Model (DOM).
DOM adalah representasi internal halaman dari browser, serta struktur data dan API yang dapat berinteraksi dengan developer web melalui JavaScript.
Mengurai dokumen HTML ke DOM ditentukan oleh
Standar HTML. Anda mungkin memperhatikan bahwa memasukkan HTML ke browser tidak akan menghasilkan error. Misalnya, tag </p>
penutup yang tidak ada adalah HTML yang valid. Markup yang salah seperti Hi! <b>I'm <i>Chrome</b>!</i>
(tag b ditutup sebelum tag i) dianggap seolah-olah Anda menulis
Hi! <b>I'm <i>Chrome</i></b><i>!</i>
. Hal ini karena spesifikasi HTML didesain untuk
menangani error tersebut dengan baik. Jika ingin tahu bagaimana semua hal ini dilakukan, Anda dapat membaca bagian
"An introduction to error handle and mendapati case in the parser"
di spesifikasi HTML.
Memuat subresource
Situs biasanya menggunakan resource eksternal seperti gambar, CSS, dan JavaScript. File tersebut perlu
dimuat dari jaringan atau cache. Thread utama dapat memintanya satu per satu karena menemukannya
saat mengurai untuk mem-build DOM, tetapi untuk mempercepat, "pemindai pramuat" dijalankan secara serentak.
Jika ada hal seperti <img>
atau <link>
dalam dokumen HTML, pemindai pramuat akan mengintip token
yang dihasilkan oleh parser HTML dan mengirimkan permintaan ke thread jaringan dalam proses browser.
JavaScript dapat memblokir penguraian
Saat menemukan tag <script>
, parser HTML akan menjeda penguraian dokumen HTML dan harus
memuat, mengurai, serta mengeksekusi kode JavaScript. Mengapa? karena JavaScript dapat mengubah bentuk dokumen menggunakan hal seperti document.write()
yang mengubah seluruh struktur DOM (ringkasan model penguraian di spesifikasi HTML memiliki diagram yang bagus). Inilah sebabnya mengapa parser HTML harus menunggu JavaScript
dijalankan sebelum dapat melanjutkan penguraian dokumen HTML. Jika Anda ingin tahu tentang apa yang terjadi dalam
eksekusi JavaScript, tim V8 telah membahasnya dan membuat postingan blog tentang hal ini.
Petunjuk ke browser bagaimana Anda ingin memuat resource
Ada banyak cara developer web dapat mengirimkan petunjuk ke browser untuk memuat resource dengan baik.
Jika JavaScript tidak menggunakan document.write()
, Anda dapat menambahkan atribut async
atau defer
ke tag <script>
. Selanjutnya, browser memuat dan menjalankan kode JavaScript secara asinkron dan tidak memblokir penguraian. Anda juga dapat menggunakan modul JavaScript jika sesuai. <link rel="preload">
adalah cara untuk memberi tahu browser bahwa resource sangat diperlukan untuk navigasi saat ini dan Anda ingin mendownload sesegera mungkin. Anda dapat membaca hal ini selengkapnya di Prioritas Resource – Menggunakan Browser untuk Membantu Anda.
Penghitungan gaya
Memiliki DOM tidak cukup untuk mengetahui tampilan halaman karena kita dapat menata gaya elemen halaman di CSS. Thread utama akan mengurai CSS dan menentukan gaya yang dihitung untuk setiap node DOM. Bagian ini
adalah informasi tentang jenis gaya yang diterapkan ke setiap elemen berdasarkan pemilih CSS. Anda dapat melihat
informasi ini di bagian computed
pada DevTools.
Bahkan jika Anda tidak memberikan CSS apa pun, setiap simpul DOM memiliki gaya yang dihitung. Tag <h1>
ditampilkan
lebih besar dari tag <h2>
dan margin ditentukan untuk setiap elemen. Hal ini karena browser memiliki
style sheet default. Jika ingin mengetahui seperti apa CSS default Chrome,
Anda dapat melihat kode sumbernya di sini.
Tata Letak
Sekarang proses perender mengetahui struktur dokumen dan gaya untuk setiap node, tetapi itu tidak cukup untuk merender halaman. Bayangkan Anda mencoba mendeskripsikan lukisan kepada teman Anda melalui ponsel. "Ada lingkaran merah besar dan kotak biru kecil" tidak cukup informasi bagi teman Anda untuk mengetahui seperti apa tepatnya lukisan itu nantinya.
Tata letak adalah proses untuk menemukan geometri elemen. Thread utama menelusuri DOM dan
gaya terkomputasi, serta membuat hierarki tata letak yang berisi informasi seperti koordinat x y dan ukuran
kotak pembatas. Hierarki tata letak mungkin mirip dengan hierarki DOM, tetapi hanya berisi informasi
yang terkait dengan apa yang terlihat di halaman. Jika display: none
diterapkan, elemen tersebut bukan bagian dari
hierarki tata letak (tetapi, elemen dengan visibility: hidden
akan ada dalam hierarki tata letak). Demikian pula,
jika class pseudo dengan konten seperti p::before{content:"Hi!"}
diterapkan, class tersebut akan disertakan dalam
hierarki tata letak meskipun tidak ada dalam DOM.
Menentukan Tata Letak laman adalah tugas yang menantang. Tata letak halaman yang paling sederhana seperti aliran blok dari atas ke bawah pun harus mempertimbangkan seberapa besar font dan letak pemisah baris karena hal itu memengaruhi ukuran dan bentuk paragraf; yang kemudian memengaruhi posisi paragraf berikutnya.
CSS dapat membuat elemen mengambang di satu sisi, menyamarkan item tambahan, dan mengubah arah penulisan. Anda dapat membayangkan, tahap tata letak ini memiliki tugas yang luar biasa. Di Chrome, seluruh tim engineer mengerjakan tata letak. Jika Anda ingin melihat detail karya mereka, beberapa diskusi dari BlinkOn Conference direkam dan cukup menarik untuk ditonton.
Cat
Memiliki DOM, gaya, dan tata letak masih belum cukup untuk merender halaman. Katakanlah Anda mencoba mereproduksi sebuah lukisan. Anda mengetahui ukuran, bentuk, dan lokasi elemen, tetapi Anda masih harus menilai dalam urutan apa Anda melukisnya.
Misalnya, z-index
mungkin ditetapkan untuk elemen tertentu, dalam hal ini, menggambar dengan urutan
elemen yang ditulis dalam HTML akan menyebabkan rendering yang salah.
Pada langkah menggambar ini, thread utama akan menjalankan hierarki tata letak untuk membuat catatan paint. Paint record adalah
catatan proses menggambar seperti "latar belakang terlebih dahulu, lalu teks, lalu persegi panjang". Jika Anda pernah menggambar pada
elemen <canvas>
menggunakan JavaScript, proses ini mungkin tidak asing bagi Anda.
Memperbarui pipeline rendering membutuhkan biaya besar
Hal terpenting yang perlu dipahami dalam pipeline rendering adalah bahwa pada setiap langkah, hasil dari operasi sebelumnya digunakan untuk membuat data baru. Misalnya, jika ada perubahan pada hierarki tata letak, urutan Paint perlu dibuat ulang untuk bagian dokumen yang terpengaruh.
Jika Anda menganimasikan elemen, browser harus menjalankan operasi ini di antara setiap bingkai. Sebagian besar layar kami memuat ulang layar 60 kali per detik (60 fps); animasi akan tampak halus bagi mata manusia saat Anda memindahkan sesuatu di layar pada setiap frame. Namun, jika animasi kehilangan frame di antaranya, halaman akan terlihat "jank".
Meskipun operasi rendering Anda dapat mengimbangi refresh layar, penghitungan ini berjalan di thread utama, yang berarti dapat diblokir saat aplikasi Anda menjalankan JavaScript.
Anda dapat membagi operasi JavaScript menjadi beberapa bagian kecil dan menjadwalkan untuk berjalan di setiap frame menggunakan
requestAnimationFrame()
. Untuk mendapatkan informasi lebih lanjut tentang topik ini, lihat Mengoptimalkan Eksekusi JavaScript. Anda juga dapat menjalankan JavaScript di Web Workers
untuk menghindari pemblokiran thread utama.
Pengomposisian
Bagaimana Anda akan menggambar halaman?
Setelah browser mengetahui struktur dokumen, gaya setiap elemen, geometri halaman, dan urutan warna, bagaimana cara menggambar halaman? Mengubah informasi ini menjadi piksel pada layar disebut rasterisasi.
Mungkin cara yang naif untuk menangani hal ini adalah dengan melakukan raster bagian di dalam area pandang. Jika pengguna men-scroll halaman, pindahkan frame raster, dan isi bagian yang hilang dengan melakukan raster lagi. Inilah cara Chrome menangani rasterisasi saat pertama kali dirilis. Namun, browser modern menjalankan proses yang lebih canggih yang disebut komposisi.
Apa itu pengomposisian
Komposisi adalah teknik untuk memisahkan bagian halaman menjadi beberapa lapisan, merasterisasinya secara terpisah, dan menggabungkan sebagai halaman dalam thread terpisah yang disebut thread compositor. Jika scroll terjadi, karena lapisan sudah dirasterisasi, yang harus dilakukan hanyalah menggabungkan frame baru. Animasi dapat dilakukan dengan cara yang sama dengan memindahkan lapisan dan menggabungkan frame baru.
Anda dapat melihat bagaimana situs Anda dibagi menjadi beberapa lapisan di DevTools menggunakan panel Lapisan.
Membagi menjadi beberapa lapisan
Untuk mengetahui elemen mana yang perlu berada di lapisan mana, thread utama akan melewati
hierarki tata letak untuk membuat hierarki lapisan (bagian ini disebut "Update Layer Tree" di
panel performa DevTools). Jika bagian tertentu dari halaman yang seharusnya berupa lapisan terpisah (seperti menu
sisi geser) tidak mendapatkan lapisan tersebut, Anda dapat menunjukkannya ke browser menggunakan atribut will-change
di CSS.
Anda mungkin tergoda untuk memberikan lapisan ke setiap elemen, tetapi pengomposisian di seluruh lapisan yang berlebihan dapat menyebabkan operasi yang lebih lambat daripada meraster bagian-bagian kecil halaman setiap frame. Oleh karena itu, Anda harus mengukur performa rendering aplikasi. Untuk informasi lebih lanjut tentang topik, lihat Berpegang pada Properti Khusus Compositor dan Mengelola Jumlah Lapisan.
Raster dan komposit dari thread utama
Setelah hierarki lapisan dibuat dan urutan paint ditentukan, thread utama akan meng-commit informasi tersebut ke thread compositor. Thread compositor kemudian merasterisasi setiap lapisan. Lapisan dapat berukuran besar seperti keseluruhan panjang halaman sehingga thread compositor membaginya menjadi kartu dan mengirimkan setiap kartu ke thread raster. Thread raster melakukan rasterisasi setiap kartu dan menyimpannya dalam memori GPU.
Thread compositor dapat memprioritaskan thread raster yang berbeda sehingga hal-hal dalam area pandang (atau di dekatnya) dapat diraster terlebih dahulu. Lapisan juga memiliki beberapa penyusunan ubin untuk resolusi yang berbeda guna menangani hal-hal seperti tindakan zoom.
Setelah kartu diraster, thread compositor akan mengumpulkan informasi kartu yang disebut persegi gambar untuk membuat frame compositor.
Menggambar segi empat | Berisi informasi seperti lokasi kartu di memori dan tempat menggambar kartu di halaman dengan mempertimbangkan komposisi halaman. |
Frame compositor | Kumpulan persegi panjang gambar yang mewakili bingkai halaman. |
Frame compositor kemudian dikirim ke proses browser melalui IPC. Pada tahap ini, frame compositor lain dapat ditambahkan dari UI thread untuk perubahan UI browser atau dari proses perender lain untuk ekstensi. Frame compositor ini dikirim ke GPU untuk menampilkannya di layar. Jika peristiwa scroll terjadi, thread compositor akan membuat frame compositor lain untuk dikirim ke GPU.
Manfaat komposisi adalah dilakukan tanpa melibatkan thread utama. Thread compiler tidak perlu menunggu penghitungan gaya atau eksekusi JavaScript. Inilah sebabnya pengomposisian animasi saja dianggap terbaik untuk performa yang lancar. Jika tata letak atau paint perlu dihitung lagi, thread utama harus dilibatkan.
Rangkuman
Dalam postingan ini, kita melihat pipeline rendering dari penguraian hingga pengomposisian. Semoga, Anda sekarang diberdayakan untuk membaca lebih lanjut tentang pengoptimalan kinerja situs web.
Pada postingan berikutnya dan terakhir dari rangkaian ini, kita akan melihat thread compositor secara lebih detail dan melihat apa yang terjadi saat input pengguna seperti mouse move
dan click
masuk.
Apakah Anda menikmati postingan itu? Jika ada pertanyaan atau saran untuk postingan mendatang, saya ingin mendengar pendapat Anda di bagian komentar di bawah atau @kosamari di Twitter.