Rendering di Web

Salah satu keputusan inti yang harus dibuat developer web adalah tempat menerapkan logika dan rendering dalam aplikasi mereka. Hal ini bisa jadi sulit karena ada begitu banyak cara untuk membuat situs.

Pemahaman kami tentang ruang ini didasarkan pada pekerjaan kami di Chrome yang berkomunikasi dengan situs besar selama beberapa tahun terakhir. Secara umum, sebaiknya developer mempertimbangkan rendering sisi server atau rendering statis daripada pendekatan rehidrasi penuh.

Untuk lebih memahami arsitektur yang kita pilih saat membuat keputusan ini, kita memerlukan pemahaman yang kuat tentang setiap pendekatan dan terminologi yang konsisten untuk digunakan saat membahasnya. Perbedaan antara pendekatan rendering membantu menggambarkan kompromi rendering di web dari perspektif performa halaman.

Terminologi

Pertama, kita menentukan beberapa terminologi yang akan digunakan.

Rendering

Rendering sisi server (SSR)
Merender aplikasi di server untuk mengirim HTML, bukan JavaScript, ke klien.
Rendering sisi klien (CSR)
Merender aplikasi di browser, menggunakan JavaScript untuk mengubah DOM.
Rehidrasi
"Mem-booting" tampilan JavaScript di klien sehingga dapat menggunakan kembali hierarki dan data DOM HTML yang dirender server.
Pra-rendering
Menjalankan aplikasi sisi klien pada waktu build untuk mengambil status awalnya sebagai HTML statis.

Performa

Time to First Byte (TTFB)
Waktu antara mengklik link dan byte pertama konten yang dimuat di halaman baru.
First Contentful Paint (FCP)
Waktu saat konten yang diminta (isi artikel, dll.) menjadi terlihat.
Interaction to Next Paint (INP)
Metrik perwakilan yang menilai apakah halaman secara konsisten merespons dengan cepat terhadap input pengguna.
Total Blocking Time (TBT)
Metrik proxy untuk INP yang menghitung berapa lama thread utama diblokir selama pemuatan halaman.

Rendering sisi server

Rendering sisi server menghasilkan HTML lengkap untuk halaman di server sebagai respons terhadap navigasi. Hal ini menghindari perjalanan bolak-balik tambahan untuk pengambilan data dan template di klien, karena perender menanganinya sebelum browser mendapatkan respons.

Rendering sisi server biasanya menghasilkan FCP yang cepat. Dengan menjalankan logika halaman dan rendering di server, Anda dapat menghindari pengiriman banyak JavaScript ke klien. Hal ini membantu mengurangi TBT halaman, yang juga dapat menyebabkan INP yang lebih rendah, karena thread utama tidak sering diblokir selama pemuatan halaman. Jika thread utama sering kali tidak diblokir, interaksi pengguna memiliki lebih banyak peluang untuk berjalan lebih cepat. Hal ini masuk akal, karena dengan rendering sisi server, Anda hanya mengirim teks dan link ke browser pengguna. Pendekatan ini dapat berfungsi dengan baik untuk berbagai kondisi perangkat dan jaringan, serta membuka pengoptimalan browser yang menarik seperti penguraian dokumen streaming.

Diagram
    yang menunjukkan rendering sisi server dan eksekusi JS yang memengaruhi FCP dan TTI.
FCP dan TTI dengan rendering sisi server.

Dengan rendering sisi server, pengguna kemungkinan tidak akan menunggu lama hingga JavaScript yang terikat CPU berjalan sebelum mereka dapat menggunakan situs Anda. Meskipun Anda tidak dapat menghindari JS pihak ketiga, menggunakan rendering sisi server untuk mengurangi biaya JavaScript pihak pertama Anda sendiri dapat memberi Anda lebih banyak anggaran untuk hal lainnya. Namun, ada satu potensi konsekuensi dengan pendekatan ini: membuat halaman di server memerlukan waktu, yang dapat meningkatkan TTFB halaman Anda.

Apakah rendering sisi server cukup untuk aplikasi Anda sebagian besar bergantung pada jenis pengalaman yang Anda buat. Ada perdebatan lama tentang penerapan rendering sisi server yang benar versus rendering sisi klien, tetapi Anda selalu dapat memilih untuk menggunakan rendering sisi server untuk beberapa halaman dan tidak untuk halaman lainnya. Beberapa situs telah berhasil mengadopsi teknik rendering campuran. Misalnya, server Netflix merender halaman landing yang relatif statis, sekaligus prefetching JS untuk halaman yang banyak interaksi, sehingga halaman yang dirender klien yang lebih berat ini memiliki peluang yang lebih baik untuk dimuat dengan cepat.

Banyak framework, library, dan arsitektur modern memungkinkan Anda merender aplikasi yang sama di klien dan server. Anda dapat menggunakan teknik ini untuk rendering sisi server. Namun, arsitektur tempat rendering terjadi di server dan di klien adalah class solusi mereka sendiri dengan karakteristik dan kompromi performa yang sangat berbeda. Pengguna React dapat menggunakan server DOM API atau solusi yang dibuat di atasnya seperti Next.js untuk rendering sisi server. Pengguna Vue dapat menggunakan panduan rendering sisi server atau Nuxt Vue. Angular memiliki Universal. Namun, sebagian besar solusi populer menggunakan beberapa bentuk hidrasi, jadi perhatikan pendekatan yang digunakan alat Anda.

Rendering statis

Rendering statis terjadi pada waktu build. Pendekatan ini menawarkan FCP yang cepat, dan juga TBT dan INP yang lebih rendah, selama Anda membatasi jumlah JS sisi klien di halaman. Tidak seperti rendering sisi server, rendering ini juga mencapai TTFB yang cepat secara konsisten, karena HTML untuk halaman tidak harus dibuat secara dinamis di server. Umumnya, rendering statis berarti membuat file HTML terpisah untuk setiap URL sebelumnya. Dengan respons HTML yang dibuat sebelumnya, Anda dapat men-deploy render statis ke beberapa CDN untuk memanfaatkan caching edge.

Diagram
    yang menunjukkan rendering statis dan eksekusi JS opsional yang memengaruhi FCP dan TTI.
FCP dan TTI dengan rendering statis.

Solusi untuk rendering statis tersedia dalam berbagai bentuk dan ukuran. Alat seperti Gatsby dirancang untuk membuat developer merasa aplikasi mereka dirender secara dinamis, bukan dibuat sebagai langkah build. Alat pembuatan situs statis seperti 11ty, Jekyll, dan Metalsmith menggunakan sifat statisnya, sehingga memberikan pendekatan yang lebih berbasis template.

Salah satu kelemahan rendering statis adalah rendering tersebut harus menghasilkan file HTML individual untuk setiap kemungkinan URL. Hal ini dapat menjadi tantangan atau bahkan tidak mungkin dilakukan jika Anda tidak dapat memprediksi URL tersebut sebelumnya, atau untuk situs dengan sejumlah besar halaman unik.

Pengguna React mungkin sudah terbiasa dengan Gatsby, ekspor statis Next.js, atau Navi, yang semuanya memudahkan pembuatan halaman dari komponen. Namun, rendering statis dan pra-rendering berperilaku berbeda: halaman yang dirender secara statis bersifat interaktif tanpa perlu menjalankan banyak JavaScript sisi klien, sedangkan pra-rendering meningkatkan FCP Aplikasi Web Satu Halaman yang harus di-booting di klien untuk membuat halaman benar-benar interaktif.

Jika Anda tidak yakin apakah solusi tertentu adalah rendering statis atau pra-rendering, coba nonaktifkan JavaScript dan muat halaman yang ingin Anda uji. Untuk halaman yang dirender secara statis, sebagian besar fitur interaktif masih ada tanpa JavaScript. Halaman yang dirender sebelumnya mungkin masih memiliki beberapa fitur dasar seperti link dengan JavaScript dinonaktifkan, tetapi sebagian besar halaman tidak aktif.

Pengujian berguna lainnya adalah menggunakan throttle jaringan di Chrome DevTools dan melihat jumlah download JavaScript sebelum halaman menjadi interaktif. Pra-rendering umumnya memerlukan lebih banyak JavaScript agar menjadi interaktif, dan JavaScript tersebut cenderung lebih kompleks daripada pendekatan progressive enhancement yang digunakan dalam rendering statis.

Rendering sisi server versus rendering statis

Rendering sisi server bukanlah solusi terbaik untuk semuanya, karena sifatnya yang dinamis dapat memiliki biaya overhead komputasi yang signifikan. Banyak solusi rendering sisi server yang tidak melakukan flush lebih awal, menunda TTFB, atau menggandakan data yang dikirim (misalnya, status inline yang digunakan oleh JavaScript di klien). Di React, renderToString() dapat lambat karena bersifat sinkron dan single-threaded. DOM API server React yang lebih baru mendukung streaming, yang dapat membuat bagian awal respons HTML lebih cepat di browser sementara bagian lainnya masih dibuat di server.

Mendapatkan rendering sisi server yang "tepat" dapat melibatkan menemukan atau membuat solusi untuk caching komponen, mengelola penggunaan memori, menggunakan teknik memoisasi, dan masalah lainnya. Anda sering memproses atau mem-build ulang aplikasi yang sama dua kali, sekali di klien dan sekali di server. Rendering sisi server yang menampilkan konten lebih cepat tidak selalu membuat Anda melakukan lebih sedikit pekerjaan. Jika Anda memiliki banyak pekerjaan di klien setelah respons HTML yang dibuat server tiba di klien, hal ini masih dapat menyebabkan TBT dan INP yang lebih tinggi untuk situs Anda.

Rendering sisi server menghasilkan HTML sesuai permintaan untuk setiap URL, tetapi dapat lebih lambat daripada hanya menayangkan konten yang dirender secara statis. Jika Anda dapat melakukan pekerjaan tambahan, rendering sisi server ditambah cache HTML dapat mengurangi waktu render server secara signifikan. Keuntungan rendering sisi server adalah kemampuan untuk mengambil lebih banyak data "live" dan merespons kumpulan permintaan yang lebih lengkap daripada yang dapat dilakukan dengan rendering statis. Halaman yang memerlukan personalisasi adalah contoh konkret dari jenis permintaan yang tidak berfungsi dengan baik dengan rendering statis.

Rendering sisi server juga dapat memberikan keputusan yang menarik saat mem-build PWA: apakah lebih baik menggunakan cache pekerja layanan halaman penuh, atau hanya merender setiap bagian konten di server?

Rendering sisi klien

Rendering sisi klien berarti merender halaman langsung di browser dengan JavaScript. Semua logika, pengambilan data, pembuatan template, dan perutean ditangani di klien, bukan di server. Hasil yang efektif adalah lebih banyak data yang diteruskan ke perangkat pengguna dari server, dan hal itu disertai dengan kumpulan kompromi tersendiri.

Rendering sisi klien dapat sulit dibuat dan dijaga agar tetap cepat untuk perangkat seluler. Dengan sedikit pekerjaan untuk mempertahankan anggaran JavaScript yang ketat dan memberikan nilai dalam perjalanan bolak-balik sedikit mungkin, Anda bisa membuat rendering sisi klien hampir mereplikasi performa rendering sisi server murni. Anda dapat membuat parser berfungsi lebih cepat dengan mengirimkan skrip dan data penting menggunakan <link rel=preload> Sebaiknya pertimbangkan juga untuk menggunakan pola seperti PRPL untuk memastikan navigasi awal dan berikutnya terasa instan.

Diagram
    yang menunjukkan rendering sisi klien yang memengaruhi FCP dan TTI.
FCP dan TTI dengan rendering sisi klien.

Kelemahan utama rendering sisi klien adalah jumlah JavaScript yang diperlukan cenderung meningkat seiring berkembangnya aplikasi, yang dapat memengaruhi INP halaman. Hal ini menjadi sangat sulit dengan penambahan library JavaScript baru, polyfill, dan kode pihak ketiga, yang bersaing untuk mendapatkan daya pemrosesan dan sering kali harus diproses sebelum konten halaman dapat dirender.

Pengalaman yang menggunakan rendering sisi klien dan mengandalkan paket JavaScript yang besar harus mempertimbangkan pemisahan kode agresif untuk menurunkan TBT dan INP selama pemuatan halaman, serta JavaScript pemuatan lambat untuk hanya menayangkan hal yang diperlukan pengguna, saat diperlukan. Untuk pengalaman dengan sedikit atau tanpa interaktivitas, rendering sisi server dapat mewakili solusi yang lebih skalabel untuk masalah ini.

Bagi orang yang mem-build aplikasi web satu halaman, mengidentifikasi bagian inti antarmuka pengguna yang dibagikan oleh sebagian besar halaman memungkinkan Anda menerapkan teknik caching shell aplikasi. Jika digabungkan dengan pekerja layanan, hal ini dapat secara drastis meningkatkan performa yang dirasakan pada kunjungan berulang, karena halaman dapat memuat HTML shell aplikasi dan dependensinya dari CacheStorage dengan sangat cepat.

Rehidrasi menggabungkan rendering sisi server dan sisi klien

Rehidrasi adalah pendekatan yang mencoba meminimalkan kompromi antara rendering sisi klien dan sisi server dengan melakukan keduanya. Permintaan navigasi seperti pemuatan halaman penuh atau memuat ulang ditangani oleh server yang merender aplikasi ke HTML, lalu JavaScript dan data yang digunakan untuk rendering disematkan ke dalam dokumen yang dihasilkan. Jika dilakukan dengan hati-hati, hal ini akan menghasilkan FCP yang cepat seperti rendering sisi server, lalu "mengambil" dengan merender lagi di klien. Ini adalah solusi yang efektif, tetapi dapat memiliki kelemahan performa yang cukup besar.

Kelemahan utama rendering sisi server dengan rehidrasi adalah dapat memiliki dampak negatif yang signifikan pada TBT dan INP, meskipun meningkatkan FCP. Halaman yang dirender sisi server dapat tampak dimuat dan interaktif, tetapi tidak dapat merespons input hingga skrip sisi klien untuk komponen dijalankan dan pengendali peristiwa telah dilampirkan. Di perangkat seluler, hal ini dapat memerlukan beberapa menit, yang membingungkan dan membuat pengguna frustrasi.

Masalah rehidrasi: satu aplikasi dengan harga dua

Agar JavaScript sisi klien dapat "melanjutkan" dengan akurat dari tempat server berhenti, tanpa meminta ulang semua data yang digunakan server untuk merender HTML-nya, sebagian besar solusi rendering sisi server akan melakukan serialisasi respons dari dependensi data UI sebagai tag skrip dalam dokumen. Karena hal ini menduplikasi banyak HTML, rehidrasi dapat menyebabkan lebih banyak masalah daripada hanya interaktivitas yang tertunda.

Dokumen HTML
    yang berisi UI serial, data inline, dan skrip bundle.js
Kode duplikat dalam dokumen HTML.

Server menampilkan deskripsi UI aplikasi sebagai respons terhadap permintaan navigasi, tetapi juga menampilkan data sumber yang digunakan untuk menyusun UI tersebut, dan salinan lengkap implementasi UI yang kemudian di-booting di klien. UI tidak menjadi interaktif hingga setelah bundle.js selesai memuat dan mengeksekusi.

Metrik performa yang dikumpulkan dari situs sungguhan menggunakan rendering dan rehidrasi sisi server menunjukkan bahwa hal ini jarang merupakan opsi terbaik. Alasan terpentingnya adalah pengaruhnya terhadap pengalaman pengguna, saat halaman terlihat siap, tetapi tidak ada fitur interaktif yang berfungsi.

Diagram
    yang menunjukkan rendering klien yang berdampak negatif pada TTI.
Efek rendering sisi klien terhadap TTI.

Namun, ada harapan untuk rendering sisi server dengan rehidrasi. Dalam jangka pendek, hanya menggunakan rendering sisi server untuk konten yang sangat dapat di-cache dapat mengurangi TTFB, menghasilkan hasil yang serupa dengan pra-rendering. Menghidrasi ulang secara bertahap, progresif, atau sebagian mungkin merupakan kunci untuk membuat teknik ini lebih layak di masa mendatang.

Streaming rendering sisi server dan rehidrasi secara bertahap

Rendering sisi server telah mengalami sejumlah perkembangan selama beberapa tahun terakhir.

Rendering sisi server streaming memungkinkan Anda mengirim HTML dalam potongan yang dapat dirender secara progresif oleh browser saat diterima. Hal ini dapat mempercepat pengiriman markup kepada pengguna, sehingga mempercepat FCP Anda. Di React, streaming yang bersifat asinkron di renderToPipeableStream(), dibandingkan dengan renderToString() sinkron, berarti backpressure ditangani dengan baik.

Rehidrasi progresif juga patut dipertimbangkan, dan React telah menerapkannya. Dengan pendekatan ini, setiap bagian aplikasi yang dirender server akan "di-booting" seiring waktu, bukan pendekatan umum saat ini untuk melakukan inisialisasi seluruh aplikasi sekaligus. Hal ini dapat membantu mengurangi jumlah JavaScript yang diperlukan untuk membuat halaman interaktif, karena memungkinkan Anda menunda upgrade sisi klien bagian halaman dengan prioritas rendah untuk mencegahnya memblokir thread utama, sehingga interaksi pengguna dapat terjadi lebih cepat setelah pengguna memulainya.

Rehidrasi progresif juga dapat membantu Anda menghindari salah satu kesalahan rehidrasi rendering sisi server yang paling umum: hierarki DOM yang dirender server akan dihancurkan, lalu segera dibuat ulang, biasanya karena render sisi klien sinkron awal memerlukan data yang belum siap, sering kali berupa Promise yang belum di-resolve.

Rehidrasi sebagian

Rehidrasi sebagian terbukti sulit diterapkan. Pendekatan ini adalah ekstensi rehidrasi progresif yang menganalisis setiap bagian halaman (komponen, tampilan, atau hierarki) dan mengidentifikasi bagian dengan sedikit interaktivitas atau tanpa reaktifitas. Untuk setiap bagian yang sebagian besar statis ini, kode JavaScript yang sesuai kemudian diubah menjadi referensi inert dan fitur dekoratif, sehingga mengurangi jejak sisi klien menjadi hampir nol.

Pendekatan hidrasi parsial memiliki masalah dan komprominya sendiri. Hal ini menyebabkan beberapa tantangan menarik untuk penyimpanan dalam cache, dan navigasi sisi klien berarti kita tidak dapat mengasumsikan bahwa HTML yang dirender server untuk bagian aplikasi yang tidak aktif tersedia tanpa pemuatan halaman penuh.

Rendering trisomorfik

Jika petugas layanan merupakan opsi untuk Anda, pertimbangkan rendering trisomorfik. Ini adalah teknik yang memungkinkan Anda menggunakan rendering sisi server streaming untuk navigasi awal atau non-JS, lalu meminta pekerja layanan Anda untuk melakukan rendering HTML untuk navigasi setelah diinstal. Hal ini dapat membuat komponen dan template yang di-cache selalu terbaru serta mengaktifkan navigasi bergaya SPA untuk merender tampilan baru dalam sesi yang sama. Pendekatan ini berfungsi optimal jika Anda dapat membagikan kode pembuatan template dan perutean yang sama antara server, halaman klien, dan pekerja layanan.

Rendering trisomorfik, yang menampilkan browser dan pekerja layanan yang berkomunikasi dengan server.
Diagram cara kerja rendering trisomorfik.

Pertimbangan SEO

Saat memilih strategi rendering web, tim sering kali mempertimbangkan dampak SEO. Rendering sisi server adalah pilihan populer untuk memberikan pengalaman "tampilan lengkap" yang dapat ditafsirkan oleh crawler. Crawler dapat memahami JavaScript, tetapi sering kali ada batasan terhadap cara rendernya. Rendering sisi klien dapat berfungsi, tetapi sering kali memerlukan pengujian dan overhead tambahan. Baru-baru ini, rendering dinamis juga menjadi opsi yang patut dipertimbangkan jika arsitektur Anda sangat bergantung pada JavaScript sisi klien.

Jika ragu, alat pengujian situs mobile-friendly adalah cara yang bagus untuk menguji apakah pendekatan yang Anda pilih dapat memberikan hasil yang Anda harapkan. Alat ini menampilkan pratinjau visual tentang tampilan halaman apa pun bagi crawler Google, konten HTML serial yang ditemukannya setelah JavaScript dijalankan, dan error apa pun yang ditemukan selama rendering.

UI Pengujian Situs Mobile-Friendly.
UI Pengujian Situs Mobile-Friendly.

Kesimpulan

Saat menentukan pendekatan untuk rendering, ukur dan pahami bottleneck Anda. Pertimbangkan apakah rendering statis atau rendering sisi server dapat membantu Anda mencapai tujuan. Tidak masalah jika sebagian besar mengirim HTML dengan JavaScript minimal untuk mendapatkan pengalaman yang interaktif. Berikut adalah infografis praktis yang menunjukkan spektrum server-klien:

Infografis yang menunjukkan spektrum opsi yang dijelaskan dalam artikel ini.
Opsi rendering dan konsekuensinya.

Kredit

Terima kasih kepada semua orang atas ulasan dan inspirasi mereka:

Jeffrey Posnick, Houssein Djirdeh, Shubhie Panicker, Chris Harrelson, dan Sebastian Markbåge