Rendering di Web

Salah satu keputusan inti yang harus diambil developer web adalah di mana menerapkan logika dan rendering dalam aplikasi mereka. Ini bisa menjadi sulit karena ada begitu banyak cara untuk membangun situs web.

Pemahaman kami tentang ruang ini didasarkan pada upaya kami di Chrome yang berbicara 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 dipilih 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

Rendering

Rendering sisi server (SSR)
Merender aplikasi universal atau sisi klien ke HTML di server.
Rendering sisi klien (CSR)
Merender aplikasi di browser menggunakan JavaScript untuk mengubah DOM.
Rehidrasi
Tampilan JavaScript "Booting" di klien sehingga akan menggunakan kembali hierarki DOM dan data HTML yang dirender server.
Pra-rendering
Menjalankan aplikasi sisi klien pada waktu build untuk menangkap status awalnya sebagai HTML statis.

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.) akan terlihat.
Interaksi ke 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 akan menghindari round trip tambahan untuk pengambilan data dan template pada klien, karena perender menanganinya sebelum browser mendapatkan respons.

Rendering sisi server umumnya menghasilkan FCP yang cepat. Menjalankan logika halaman dan rendering di server memungkinkan Anda 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 lebih jarang diblokir, interaksi pengguna memiliki lebih banyak peluang untuk berjalan lebih cepat. Hal ini masuk akal, karena dengan rendering sisi server, Anda sebenarnya 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 cenderung tidak akan menunggu JavaScript yang terikat CPU dijalankan sebelum dapat menggunakan situs Anda. Meskipun Anda tidak dapat menghindari JS pihak ketiga, penggunaan rendering sisi server untuk mengurangi biaya JavaScript pihak pertama dapat memberi Anda lebih banyak anggaran untuk sisanya. Namun, ada satu potensi kompromi dengan pendekatan ini: membuat halaman di server membutuhkan waktu, yang dapat meningkatkan TTFB halaman Anda.

Apakah rendering sisi server cukup untuk aplikasi Anda sangat bergantung pada jenis pengalaman yang Anda bangun. Ada perdebatan lama tentang aplikasi rendering sisi server yang benar versus rendering sisi klien, tetapi Anda selalu dapat memilih untuk menggunakan rendering sisi server untuk beberapa halaman, tidak yang lainnya. Beberapa situs telah menerapkan teknik rendering campuran dengan sukses. Misalnya, server Netflix merender halaman landing yang relatif statis, sementara pengambilan data JS untuk halaman dengan banyak interaksi, sehingga halaman yang lebih berat dan dirender oleh klien ini akan lebih berpeluang untuk dimuat dengan cepat.

Banyak framework, library, dan arsitektur modern yang memungkinkan Anda merender aplikasi yang sama pada klien dan server. Anda dapat menggunakan teknik ini untuk rendering sisi server. Namun, arsitektur dengan rendering yang terjadi di server dan di klien adalah kelas solusinya sendiri dengan karakteristik performa dan kompromi yang sangat berbeda. Pengguna React dapat menggunakan DOM API server atau solusi yang dibangun padanya seperti Next.js untuk rendering sisi server. Pengguna Vue dapat menggunakan panduan rendering sisi server atau Nuxt Vue. Angular memiliki Universal. Namun, solusi yang paling populer menggunakan beberapa bentuk hidrasi, jadi berhati-hatilah dengan 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 JS sisi klien di halaman Anda. Tidak seperti rendering sisi server, proses ini juga mencapai TTFB yang cepat secara konsisten, karena HTML untuk halaman tidak harus dibuat secara dinamis di server. Umumnya, rendering statis berarti menghasilkan file HTML terpisah untuk setiap URL sebelum waktunya. Dengan respons HTML yang dihasilkan terlebih dahulu, Anda dapat menerapkan render statis ke beberapa CDN untuk memanfaatkan edge caching.

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 didesain untuk membuat developer merasa aplikasi mereka dirender secara dinamis, bukan dihasilkan sebagai langkah build. Alat pembuatan situs statis seperti 11ty, Jekyll, dan Metalsmith melibatkan sifat statisnya, sehingga memberikan pendekatan yang lebih berbasis template.

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

Pengguna React mungkin sudah tidak asing dengan Gatsby, ekspor statis Next.js, atau Navi, yang semuanya memudahkan pembuatan halaman dari komponen. Namun, perilaku rendering statis dan pra-rendering memiliki perilaku yang 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 pada klien agar halaman menjadi benar-benar interaktif.

Jika tidak yakin apakah solusi yang diberikan 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 pra-rendering mungkin masih memiliki beberapa fitur dasar seperti link dengan JavaScript dinonaktifkan, tetapi sebagian besar halaman tersebut tidak aktif.

Pengujian berguna lainnya adalah menggunakan throttling jaringan di Chrome DevTools dan melihat jumlah download JavaScript sebelum halaman menjadi interaktif. Pra-rendering umumnya memerlukan lebih banyak JavaScript untuk menjadi interaktif, dan bahwa JavaScript 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 semua hal, karena sifat dinamisnya dapat menimbulkan biaya overhead komputasi yang signifikan. Banyak solusi rendering sisi server yang tidak menghapus data lebih awal, menunda TTFB, atau menggandakan data yang dikirim (misalnya, status inline yang digunakan oleh JavaScript pada klien). Di React, renderToString() dapat menjadi lambat karena bersifat sinkron dan thread tunggal. API DOM server React yang lebih baru mendukung streaming, yang dapat memperoleh bagian awal dari respons HTML ke browser lebih cepat sementara bagian lainnya masih dibuat di server.

Agar proses rendering sisi server menjadi "benar" dapat dilakukan dengan menemukan atau membuat solusi untuk cache komponen, mengelola konsumsi 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 dalam waktu singkat tidak berarti pekerjaan Anda lebih sedikit. Jika Anda memiliki banyak pekerjaan di klien setelah respons HTML yang dibuat server diterima 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 dibandingkan hanya menayangkan konten yang dirender statis. Jika Anda dapat melakukan pekerjaan tambahan, rendering sisi server dan caching HTML dapat mengurangi waktu render server secara signifikan. Keuntungan rendering sisi server adalah kemampuan untuk menarik lebih banyak data "aktif" dan merespons kumpulan permintaan yang lebih lengkap daripada yang mungkin dilakukan dengan rendering statis. Halaman yang memerlukan personalisasi adalah contoh konkret jenis permintaan yang tidak berfungsi baik dengan rendering statis.

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

Rendering sisi klien

Rendering sisi klien berarti merender halaman secara 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 disertai dengan konsekuensi tersendiri.

Rendering sisi klien mungkin sulit dibuat dan tetap cepat untuk perangkat seluler. Dengan sedikit usaha untuk mempertahankan anggaran JavaScript yang ketat dan menghasilkan nilai dalam beberapa bolak-balik sebanyak mungkin, Anda dapat memperoleh rendering sisi klien agar hampir mereplikasi performa rendering sisi server yang murni. Anda dapat membuat parser berfungsi lebih cepat dengan mengirimkan skrip dan data penting menggunakan <link rel=preload> Sebaiknya Anda juga mempertimbangkan penggunaan 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 bertambah seiring pertumbuhan 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 secara agresif untuk menurunkan TBT dan INP selama pemuatan halaman, serta JavaScript dengan pemuatan lambat untuk menayangkan hanya hal yang dibutuhkan pengguna, saat diperlukan. Untuk pengalaman dengan sedikit atau tanpa interaktivitas, rendering sisi server dapat merepresentasikan solusi yang lebih skalabel untuk masalah ini.

Untuk orang yang mem-build aplikasi web satu halaman, mengidentifikasi bagian inti dari antarmuka pengguna yang dibagikan oleh sebagian besar halaman memungkinkan Anda menerapkan teknik caching shell aplikasi. Jika dikombinasikan dengan pekerja layanan, hal ini dapat meningkatkan performa yang dirasakan secara signifikan 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 memperlancar keseimbangan antara rendering sisi klien dan sisi server dengan melakukan keduanya. Permintaan navigasi seperti pemuatan halaman penuh atau muat ulang ditangani oleh server yang merender aplikasi ke HTML, lalu JavaScript dan data yang digunakan untuk rendering akan disematkan ke dalam dokumen yang dihasilkan. Jika dilakukan dengan hati-hati, proses ini akan mencapai FCP yang cepat seperti rendering sisi server, lalu "melanjutkan" dengan merender kembali pada klien. Ini adalah solusi yang efektif, tetapi dapat memiliki kelemahan performa yang cukup besar.

Kelemahan utama rendering sisi server dengan rehidrasi adalah hal ini dapat memiliki dampak negatif yang signifikan terhadap TBT dan INP, meskipun dapat meningkatkan FCP. Halaman yang dirender sisi server dapat terlihat dimuat dan interaktif, tetapi tidak dapat merespons input hingga skrip sisi klien untuk komponen dieksekusi dan pengendali peristiwa telah dilampirkan. Pada perangkat seluler, hal ini bisa memakan waktu beberapa menit, membingungkan dan menjengkelkan pengguna.

Masalah rehidrasi: satu aplikasi seharga dua aplikasi

Agar JavaScript sisi klien dapat "mengambil" secara akurat dari bagian yang ditinggalkan server, tanpa meminta ulang semua data yang dirender server HTML-nya, sebagian besar solusi rendering sisi server membuat 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 menunda interaktivitas.

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

Server menampilkan deskripsi UI aplikasi sebagai respons atas permintaan navigasi, tetapi juga menampilkan data sumber yang digunakan untuk menyusun UI tersebut, serta salinan lengkap implementasi UI yang kemudian melakukan booting pada klien. UI tidak menjadi interaktif hingga bundle.js selesai memuat dan dieksekusi.

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

Diagram
    yang menunjukkan rendering klien yang negatif yang memengaruhi 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 mudah disimpan dalam cache dapat mengurangi TTFB, yang memberikan hasil serupa dengan pra-rendering. Rehidrasi secara bertahap, progresif, atau sebagian mungkin menjadi kunci untuk menjadikan teknik ini lebih ber layak di masa mendatang.

Lakukan streaming rendering sisi server dan rehidrasi secara bertahap

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

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

Rehidrasi progresif juga layak dipertimbangkan, dan React telah menerapkannya. Dengan pendekatan ini, setiap bagian dari aplikasi yang dirender server akan "di-booting" dari waktu ke waktu, bukan pendekatan umum yang saat ini digunakan untuk 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 atas bagian halaman berprioritas rendah untuk mencegahnya memblokir thread utama, sehingga memungkinkan interaksi pengguna terjadi lebih cepat setelah pengguna memulainya.

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

Rehidrasi sebagian

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

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

Rendering trisomorfik

Jika pekerja layanan adalah pilihan bagi 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 melakukan rendering HTML untuk navigasi setelah diinstal. Hal ini dapat menjaga komponen dan template yang di-cache tetap terbaru dan mengaktifkan navigasi gaya SPA untuk merender tampilan baru dalam sesi yang sama. Pendekatan ini akan berfungsi optimal jika Anda dapat berbagi kode template dan perutean yang sama antara server, halaman klien, dan pekerja layanan.

Diagram rendering Trisomorphic, menunjukkan browser dan pekerja layanan yang berkomunikasi dengan server.
Diagram cara kerja rendering trisomorfik.

Pertimbangan SEO

Saat memilih strategi rendering web, tim sering 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 dalam cara merendernya. 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 untuk mobile-friendly merupakan cara yang tepat untuk menguji apakah pendekatan yang Anda pilih memberikan hasil sesuai harapan. Bagian ini menunjukkan pratinjau visual tentang bagaimana halaman apa pun ditampilkan kepada crawler Google, konten HTML serial yang ditemukannya setelah JavaScript dijalankan, dan error yang ditemukan selama rendering.

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

Kesimpulan

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

Infografis yang menunjukkan beragam 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 jelasge