Rendering di Web

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

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

Untuk lebih memahami arsitektur yang kita pilih saat membuat keputusan ini, kita memerlukan terminologi yang konsisten dan framework bersama untuk setiap pendekatan. Kemudian, Anda dapat mengevaluasi kompromi setiap pendekatan rendering dengan lebih baik dari perspektif performa halaman.

Terminologi

Pertama, kita akan menentukan beberapa terminologi yang akan kita gunakan.

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.
Pra-rendering
Menjalankan aplikasi sisi klien pada waktu build untuk merekam status awalnya sebagai HTML statis.
Hidrasi
Menjalankan skrip sisi klien untuk menambahkan status dan interaktivitas aplikasi ke HTML yang dirender server. Hidrasi mengasumsikan DOM tidak berubah.
Rehidrasi
Meskipun sering digunakan untuk memiliki arti yang sama dengan hidrasi, rehidrasi menyiratkan pembaruan DOM secara rutin dengan status terbaru, termasuk setelah hidrasi awal.

Performa

Time to First Byte (TTFB)
Waktu antara mengklik link dan byte pertama konten dimuat di halaman baru.
First Contentful Paint (FCP)
Waktu saat konten yang diminta (isi artikel, dll.) menjadi terlihat.
Interaction to Next Paint (INP)
Metrik representatif yang menilai apakah halaman secara konsisten merespons input pengguna dengan cepat.
Total Waktu Pemblokiran (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 pulang pergi tambahan untuk pengambilan data dan pembuatan template di klien, karena perender menanganinya sebelum browser mendapatkan respons.

Rendering sisi server umumnya menghasilkan FCP yang cepat. Menjalankan logika halaman dan merender di server memungkinkan Anda menghindari pengiriman banyak JavaScript ke klien. Hal ini membantu mengurangi TTBT halaman, yang juga dapat menghasilkan INP yang lebih rendah, karena thread utama tidak sering diblokir selama pemuatan halaman. Jika thread utama lebih jarang diblokir, interaksi pengguna akan memiliki lebih banyak peluang untuk berjalan lebih cepat.

Hal ini masuk akal, karena dengan rendering sisi server, Anda hanya mengirimkan 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 JavaScript yang memengaruhi FCP dan TTI.
FCP dan TTI dengan rendering sisi server.

Dengan rendering sisi server, pengguna cenderung tidak perlu menunggu JavaScript yang terikat CPU berjalan sebelum mereka dapat menggunakan situs Anda. Meskipun Anda tidak dapat menghindari JavaScript pihak ketiga, menggunakan rendering sisi server untuk mengurangi biaya JavaScript pihak pertama Anda dapat memberi Anda lebih banyak anggaran untuk yang lainnya. Namun, ada satu potensi kerugian dengan pendekatan ini: membuat halaman di server membutuhkan waktu, yang dapat meningkatkan TTFB halaman Anda.

Apakah rendering sisi server sudah cukup untuk aplikasi Anda sebagian besar bergantung pada jenis pengalaman yang Anda bangun. Ada perdebatan yang sudah berlangsung lama mengenai penerapan yang tepat dari rendering sisi server 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 menerapkan teknik rendering hybrid. Misalnya, Netflix merender halaman landing yang relatif statis di server, sekaligus prefetching JavaScript untuk halaman yang memiliki banyak interaksi, sehingga halaman yang dirender klien ini memiliki peluang lebih besar untuk dimuat dengan cepat.

Dengan banyak framework, library, dan arsitektur modern, Anda dapat merender aplikasi yang sama di klien dan server. Anda dapat menggunakan teknik ini untuk rendering sisi server. Namun, arsitektur yang melakukan rendering di server dan di klien adalah kelas solusi tersendiri dengan karakteristik performa dan kompromi yang sangat berbeda. Pengguna React dapat menggunakan API DOM server atau solusi yang dibangun di atasnya seperti Next.js untuk rendering sisi server. Pengguna Vue dapat menggunakan panduan rendering sisi server Vue atau Nuxt. 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, serta TBT dan INP yang lebih rendah, selama Anda membatasi jumlah JavaScript sisi klien di halaman Anda. Tidak seperti rendering sisi server, TTFB yang konsisten dan cepat juga dapat dicapai, karena HTML untuk halaman tidak harus dibuat secara dinamis di server. Secara umum, rendering statis berarti menghasilkan 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 JavaScript 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 bahwa aplikasi mereka dirender secara dinamis, bukan dibuat sebagai langkah build. Alat pembuatan situs statis seperti 11ty, Jekyll, dan Metalsmith mengadopsi sifat statisnya, sehingga memberikan pendekatan yang lebih berbasis template.

Salah satu kekurangan rendering statis adalah harus menghasilkan file HTML individual untuk setiap kemungkinan URL. Hal ini bisa menjadi sulit atau bahkan tidak mungkin jika Anda perlu memprediksi URL tersebut sebelumnya dan untuk situs dengan sejumlah besar halaman unik.

Pengguna React mungkin sudah familiar 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 mengeksekusi banyak JavaScript sisi klien, sedangkan pra-rendering meningkatkan FCP Aplikasi Halaman Tunggal yang harus di-boot di klien agar 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 telah di-prerender mungkin masih memiliki beberapa fitur dasar seperti link dengan JavaScript yang dinonaktifkan, tetapi sebagian besar halaman tidak aktif.

Pengujian berguna lainnya adalah menggunakan pembatasan jaringan di Chrome DevTools dan melihat seberapa banyak JavaScript yang didownload sebelum halaman menjadi interaktif. Pra-rendering umumnya memerlukan lebih banyak JavaScript agar menjadi interaktif, dan JavaScript tersebut cenderung lebih kompleks daripada pendekatan peningkatan progresif yang digunakan dalam rendering statis.

Rendering sisi server versus rendering statis

Rendering sisi server bukanlah solusi terbaik untuk semuanya, karena sifat dinamisnya dapat menimbulkan biaya overhead komputasi yang signifikan. Banyak solusi rendering sisi server 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 berjalan lambat karena bersifat sinkron dan ber-thread tunggal. API DOM server React yang lebih baru mendukung streaming, yang dapat mengirimkan bagian awal respons HTML ke browser lebih cepat saat bagian lainnya masih dibuat di server.

Mendapatkan rendering sisi server yang "benar" dapat melibatkan penemuan atau pembuatan solusi untuk penyimpanan cache komponen, pengelolaan konsumsi memori, penggunaan teknik memoization, dan masalah lainnya. Anda sering memproses atau membangun ulang aplikasi yang sama dua kali, sekali di klien dan sekali di server. Rendering sisi server yang menampilkan konten lebih cepat tidak selalu mengurangi pekerjaan Anda. Jika Anda memiliki banyak pekerjaan di sisi klien setelah respons HTML yang dihasilkan server tiba di klien, hal ini tetap 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 statis. Jika Anda dapat melakukan upaya tambahan, rendering sisi server plus penyimpanan dalam cache HTML dapat mengurangi waktu rendering server secara signifikan. Keuntungan rendering sisi server adalah kemampuan untuk menarik lebih banyak data "live" dan merespons serangkaian permintaan yang lebih lengkap daripada yang mungkin dilakukan dengan rendering statis. Halaman yang memerlukan personalisasi adalah contoh konkret jenis permintaan yang tidak berfungsi dengan baik dengan rendering statis.

Server-side rendering juga dapat menghadirkan keputusan menarik saat membangun PWA. Apakah lebih baik menggunakan penyimpanan dalam cache service worker halaman penuh, atau 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 efektifnya adalah lebih banyak data yang diteruskan ke perangkat pengguna dari server, dan hal ini memiliki serangkaian pertimbangannya sendiri.

Rendering sisi klien mungkin sulit dibuat dan dipertahankan agar tetap cepat untuk perangkat seluler. Dengan sedikit upaya untuk mempertahankan anggaran JavaScript yang ketat dan memberikan nilai dalam sesedikit mungkin perjalanan pulang pergi, Anda dapat 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 memengaruhi FCP dan TTI.
FCP dan TTI dengan rendering sisi klien.

Kelemahan utama rendering sisi klien adalah jumlah JavaScript yang diperlukan cenderung bertambah seiring pertumbuhan aplikasi, yang dapat memengaruhi INP halaman. Hal ini menjadi sangat sulit dengan penambahan library JavaScript, polyfill, dan kode pihak ketiga baru, 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 besar harus mempertimbangkan pemisahan kode agresif untuk menurunkan TBT dan INP selama pemuatan halaman, serta memuat JavaScript secara lambat untuk menayangkan hanya yang dibutuhkan pengguna, saat dibutuhkan. Untuk pengalaman dengan interaktivitas yang sedikit atau tidak ada, rendering sisi server dapat menjadi solusi yang lebih skalabel untuk masalah ini.

Untuk developer yang membuat aplikasi halaman tunggal, mengidentifikasi bagian inti antarmuka pengguna yang digunakan oleh sebagian besar halaman memungkinkan Anda menerapkan teknik penyimpanan cache shell aplikasi. Jika digabungkan dengan pekerja layanan, hal ini dapat meningkatkan performa yang dirasakan secara signifikan pada kunjungan berulang, karena halaman dapat memuat HTML dan dependensi shell aplikasi dari CacheStorage dengan sangat cepat.

Rehidrasi menggabungkan rendering sisi server dan sisi klien

Hidrasi adalah pendekatan yang mengurangi kerugian antara rendering sisi klien dan sisi server dengan melakukan keduanya. Permintaan navigasi, seperti pemuatan atau pemuatan ulang halaman penuh, ditangani oleh server yang merender aplikasi ke HTML. Kemudian, 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 menimbulkan kerugian performa yang cukup besar.

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

Masalah rehidrasi: satu aplikasi dengan harga dua aplikasi

Agar JavaScript sisi klien dapat secara akurat mengambil alih tempat server berhenti, tanpa meminta ulang semua data yang dirender HTML-nya oleh server, sebagian besar solusi rendering sisi server menserialisasi respons dari dependensi data UI sebagai tag skrip dalam dokumen. Karena hal ini menduplikasi banyak HTML, rehidrasi dapat menyebabkan lebih banyak masalah daripada hanya penundaan interaktivitas.

Dokumen HTML yang berisi UI berserial, data inline, dan skrip bundle.js.

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

Metrik performa yang dikumpulkan dari situs sungguhan menggunakan rendering sisi server dan rehidrasi menunjukkan bahwa opsi ini jarang menjadi opsi terbaik. Alasan terpentingnya adalah efeknya pada pengalaman pengguna, saat halaman terlihat siap, tetapi tidak ada fitur interaktifnya yang berfungsi.

Efek negatif rendering sisi klien pada TTI.

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, sehingga menghasilkan hasil yang serupa dengan pra-rendering. Merehidrasi secara bertahap, progresif, atau sebagian mungkin menjadi kunci untuk membuat teknik ini lebih layak pada masa mendatang.

Streaming rendering sisi server dan rehidrasi secara progresif

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

Streaming rendering sisi server memungkinkan Anda mengirim HTML dalam potongan yang dapat dirender secara progresif oleh browser saat diterima. Tindakan ini dapat mengirimkan markup kepada pengguna Anda lebih cepat, sehingga mempercepat FCP Anda. Di React, aliran yang bersifat asinkron di renderToPipeableStream(), dibandingkan dengan renderToString() sinkron, berarti tekanan balik ditangani dengan baik.

Rehidrasi progresif juga patut dipertimbangkan (React telah menerapkannya). Dengan pendekatan ini, setiap bagian aplikasi yang dirender server akan "di-booting" dari waktu ke waktu, bukan seperti pendekatan umum saat ini yang menginisialisasi seluruh aplikasi sekaligus. Hal ini dapat membantu mengurangi jumlah JavaScript yang diperlukan untuk membuat halaman menjadi interaktif, karena memungkinkan Anda menunda upgrade sisi klien pada bagian halaman yang berprioritas 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: pohon DOM yang dirender server dihancurkan, lalu segera dibangun kembali, paling sering karena rendering sisi klien sinkron awal memerlukan data yang belum siap, sering kali berupa Promise yang belum diselesaikan.

Rehidrasi sebagian

Rehidrasi sebagian terbukti sulit diterapkan. Pendekatan ini merupakan ekstensi rehidrasi progresif yang menganalisis setiap bagian halaman (komponen, tampilan, atau hierarki) dan mengidentifikasi bagian yang memiliki interaktivitas rendah atau tidak reaktif. Untuk setiap bagian yang sebagian besar statis ini, kode JavaScript yang sesuai kemudian diubah menjadi referensi inert dan fitur dekoratif, sehingga mengurangi jejak sisi kliennya menjadi hampir nol.

Pendekatan rehidrasi parsial memiliki masalah dan komprominya sendiri. Hal ini menimbulkan beberapa tantangan menarik untuk caching, 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 service worker adalah opsi bagi Anda, pertimbangkan rendering trisomorfik. Teknik ini memungkinkan Anda menggunakan server-side rendering streaming untuk navigasi awal atau non-JavaScript, lalu membuat pekerja layanan Anda melakukan rendering HTML untuk navigasi setelah diinstal. Hal ini dapat menjaga komponen dan template yang di-cache tetap terbaru dan memungkinkan navigasi gaya SPA untuk merender tampilan baru dalam sesi yang sama. Pendekatan ini berfungsi paling baik jika Anda dapat membagikan kode perutean dan pembuatan template yang sama antara server, halaman klien, dan pekerja layanan.

Render trisomorfik, yang menunjukkan browser dan pekerja layanan berkomunikasi dengan server.

Pertimbangan SEO

Saat memilih strategi rendering web, tim sering kali mempertimbangkan dampak SEO. Server-side rendering adalah pilihan populer untuk memberikan pengalaman "tampilan lengkap" yang dapat diinterpretasikan oleh crawler. Crawler dapat memahami JavaScript, tetapi sering kali ada batasan pada cara perenderannya. 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 uji mobile-friendly adalah cara yang efektif untuk menguji apakah pendekatan yang Anda pilih sesuai dengan harapan Anda. Alat ini menampilkan pratinjau visual tentang tampilan halaman bagi crawler Google, konten HTML yang diserialkan yang ditemukan setelah JavaScript dieksekusi, dan error yang terjadi selama rendering.

UI Pengujian Situs Mobile-Friendly.

Kesimpulan

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

Opsi rendering dan kelebihan serta kekurangannya.

Kredit {:#credits}

Terima kasih kepada semua orang atas ulasan dan inspirasi mereka:

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