Pengoptimalan JavaScript Start-up

Saat membuat situs yang lebih mengandalkan JavaScript, terkadang kami membayar apa yang kami kirim dengan cara yang tidak selalu mudah dilihat. Dalam artikel ini, kami akan menjelaskan mengapa sedikit disiplin dapat membantu jika Anda ingin situs dimuat dan interaktif dengan cepat di perangkat seluler. Mengirimkan lebih sedikit JavaScript dapat berarti lebih sedikit waktu dalam transmisi jaringan, lebih sedikit waktu yang dihabiskan untuk mendekompresi kode, dan lebih sedikit waktu untuk mengurai dan mengompilasi JavaScript ini.

Jaringan

Saat sebagian besar developer memikirkan biaya JavaScript, mereka memikirkannya dalam hal biaya download dan eksekusi. Semakin lambat koneksi pengguna, semakin lama waktu yang diperlukan untuk mengirim lebih banyak byte JavaScript melalui jaringan.

Saat browser meminta
resource, resource tersebut perlu diambil, lalu didekompresi. Dalam kasus
resource seperti JavaScript, resource tersebut harus diuraikan dan dikompilasi sebelum
eksekusi.

Hal ini dapat menjadi masalah, karena jenis koneksi jaringan yang efektif yang dimiliki pengguna mungkin sebenarnya bukan 3G, 4G, atau Wi-Fi. Anda dapat menggunakan Wi-Fi kedai kopi, tetapi terhubung ke hotspot seluler dengan kecepatan 2G.

Anda dapat mengurangi biaya transfer jaringan JavaScript melalui:

  • Hanya mengirim kode yang diperlukan pengguna.
    • Gunakan pemisahan kode untuk membagi JavaScript menjadi bagian yang penting dan tidak penting. Penggabung modul seperti webpack mendukung pemisahan kode.
    • Memuat kode yang tidak penting secara lambat.
  • Minifikasi
  • Kompresi
    • Minimal, gunakan gzip untuk mengompresi resource berbasis teks.
    • Sebaiknya gunakan Brotli ~q11. Brotli memiliki performa lebih baik daripada gzip dalam hal rasio kompresi. Hal ini membantu CertSimple menghemat 17% ukuran byte JS yang dikompresi dan LinkedIn menghemat 4% waktu pemuatan.
  • Menghapus kode yang tidak digunakan.
  • Menyimpan kode dalam cache untuk meminimalkan perjalanan jaringan.
    • Gunakan cache HTTP untuk memastikan browser menyimpan respons dalam cache secara efektif. Tentukan masa aktif yang optimal untuk skrip (max-age) dan token validasi pasokan (ETag) untuk menghindari transfer byte yang tidak berubah.
    • Cache Pekerja Layanan dapat membuat jaringan aplikasi Anda tangguh dan memberi Anda akses cepat ke fitur seperti cache kode V8.
    • Gunakan cache jangka panjang agar tidak perlu mengambil ulang resource yang belum berubah. Jika menggunakan Webpack, lihat hashing nama file.

Mengurai/Mengompilasi

Setelah didownload, salah satu biaya terberat JavaScript adalah waktu yang diperlukan mesin JS untuk menguraikan/mengompilasi kode ini. Di Chrome DevTools, penguraian dan kompilasi adalah bagian dari waktu "Pembuatan skrip" kuning di panel Performa.

ALT_TEXT_HERE

Tab Bottom-Up dan Call Tree menampilkan waktu Parse/kompilasi yang tepat:

ALT_TEXT_HERE
Panel Performa Chrome DevTools > Bottom-Up. Dengan mengaktifkan Statistik Panggilan Runtime V8, kita dapat melihat waktu yang dihabiskan dalam fase seperti Mengurai dan Mengompilasi

Namun, mengapa hal ini penting?

ALT_TEXT_HERE

Memerlukan waktu lama untuk mengurai/mengompilasi kode dapat sangat menunda waktu pengguna dapat berinteraksi dengan situs Anda. Makin banyak JavaScript yang Anda kirim, makin lama waktu yang diperlukan untuk mengurai dan mengompilasi JavaScript sebelum situs Anda menjadi interaktif.

Byte demi byte, JavaScript lebih mahal untuk diproses browser daripada gambar atau Font Web dengan ukuran yang setara — Tom Dale

Dibandingkan dengan JavaScript, ada banyak biaya yang terlibat dalam pemrosesan gambar berukuran setara (gambar masih harus didekode), tetapi pada hardware seluler rata-rata, JS cenderung berdampak negatif pada interaktivitas halaman.

ALT_TEXT_HERE
JavaScript dan byte gambar memiliki biaya yang sangat berbeda. Gambar biasanya tidak memblokir thread utama atau mencegah antarmuka menjadi interaktif saat didekode dan rasterisasi. Namun, JS dapat menunda interaktivitas karena biaya penguraian, kompilasi, dan eksekusi.

Saat kita membahas penguraian dan kompilasi yang lambat, konteks itu penting — kita membahas ponsel rata-rata di sini. Pengguna rata-rata dapat memiliki ponsel dengan CPU dan GPU lambat, tanpa cache L2/L3, dan bahkan mungkin memiliki memori yang terbatas.

Kemampuan jaringan dan kemampuan perangkat tidak selalu cocok. Pengguna dengan koneksi Fiber yang luar biasa belum tentu memiliki CPU terbaik untuk menguraikan dan mengevaluasi JavaScript yang dikirim ke perangkat mereka. Hal ini juga berlaku sebaliknya… koneksi jaringan yang buruk, tetapi CPU yang sangat cepat. — Kristofer Baxter, LinkedIn

Di bawah ini, kita dapat melihat biaya penguraian JavaScript yang didekompresi (sederhana) sebesar ~1 MB pada hardware kelas bawah dan kelas atas. Ada perbedaan waktu 2–5x untuk mengurai/mengompilasi kode antara ponsel tercepat di pasaran dan ponsel rata-rata.

ALT_TEXT_HERE
Grafik ini menyoroti waktu penguraian untuk paket JavaScript 1 MB (~250 KB yang di-gzip) di perangkat desktop dan seluler dari class yang berbeda. Saat melihat biaya pemrosesan, angka yang didekompresi harus dipertimbangkan, misalnya JS yang dikompresi gzip sebesar ~250 KB didekompresi menjadi kode sebesar ~1 MB.

Bagaimana dengan situs di dunia nyata, seperti CNN.com?

Di iPhone 8 kelas atas, hanya perlu waktu ~4 detik untuk mengurai/mengompilasi JS CNN dibandingkan dengan ~13 detik untuk ponsel biasa (Moto G4). Hal ini dapat secara signifikan memengaruhi kecepatan pengguna berinteraksi sepenuhnya dengan situs ini.

ALT_TEXT_HERE
Di atas, kita melihat waktu penguraian yang membandingkan performa chip A11 Bionic Apple dengan Snapdragon 617 di hardware Android yang lebih rata-rata.

Hal ini menyoroti pentingnya pengujian pada hardware rata-rata (seperti Moto G4), bukan hanya ponsel yang mungkin ada di saku Anda. Namun, konteks itu penting: optimalkan untuk kondisi perangkat dan jaringan yang dimiliki pengguna Anda.

ALT_TEXT_HERE
Google Analytics dapat memberikan insight tentang class perangkat seluler yang digunakan pengguna sebenarnya untuk mengakses situs Anda. Hal ini dapat memberikan peluang untuk memahami batasan CPU/GPU sebenarnya yang digunakan.

Apakah kita benar-benar mengirim terlalu banyak JavaScript? Mungkin saja :)

Dengan menggunakan HTTP Archive (situs teratas ~500 ribu) untuk menganalisis status JavaScript di perangkat seluler, kita dapat melihat bahwa 50% situs memerlukan waktu lebih dari 14 detik untuk menjadi interaktif. Situs ini menghabiskan hingga 4 detik hanya untuk mengurai dan mengompilasi JS.

ALT_TEXT_HERE

Pertimbangkan waktu yang diperlukan untuk mengambil dan memproses JS dan resource lainnya. Jadi, mungkin tidak mengherankan jika pengguna harus menunggu beberapa saat sebelum merasa halaman siap digunakan. Kita pasti bisa melakukan yang lebih baik di sini.

Menghapus JavaScript non-kritis dari halaman dapat mengurangi waktu transmisi, penguraian dan kompilasi yang intensif CPU, serta potensi overhead memori. Hal ini juga membantu membuat halaman Anda lebih interaktif dengan lebih cepat.

Waktu eksekusi

Tidak hanya penguraian dan kompilasi yang dapat menimbulkan biaya. Eksekusi JavaScript (menjalankan kode setelah diuraikan/dikompilasi) adalah salah satu operasi yang harus terjadi di thread utama. Waktu eksekusi yang lama juga dapat memperlambat waktu yang diperlukan pengguna untuk berinteraksi dengan situs Anda.

ALT_TEXT_HERE

Jika skrip dieksekusi selama lebih dari 50 md, waktu untuk interaktif akan tertunda oleh seluruh waktu yang diperlukan untuk mendownload, mengompilasi, dan mengeksekusi JS — Alex Russell

Untuk mengatasi hal ini, JavaScript akan diuntungkan jika berada dalam potongan kecil untuk menghindari kunci thread utama. Pelajari apakah Anda dapat mengurangi jumlah pekerjaan yang dilakukan selama eksekusi.

Biaya lainnya

JavaScript dapat memengaruhi performa halaman dengan cara lain:

  • Memori. Halaman dapat sering mengalami jank atau jeda karena GC (pembersihan sampah memori). Saat browser mengklaim kembali memori, eksekusi JS dijeda sehingga browser yang sering mengumpulkan sampah dapat menjeda eksekusi lebih sering daripada yang kita inginkan. Hindari kebocoran memori dan jeda gc yang sering terjadi agar halaman bebas jank.
  • Selama runtime, JavaScript yang berjalan lama dapat memblokir thread utama sehingga halaman tidak responsif. Memecah pekerjaan menjadi bagian-bagian yang lebih kecil (menggunakan requestAnimationFrame() atau requestIdleCallback() untuk penjadwalan) dapat meminimalkan masalah responsivitas yang dapat membantu meningkatkan Interaction to Next Paint (INP).

Pola untuk mengurangi biaya pengiriman JavaScript

Saat Anda mencoba memperlambat waktu penguraian/kompilasi dan transmisi jaringan untuk JavaScript, ada pola yang dapat membantu seperti pengelompokan berbasis rute atau PRPL.

PRPL

PRPL (Push, Render, Pre-cache, Lazy-load) adalah pola yang mengoptimalkan interaktivitas melalui pemisahan dan penyimpanan dalam cache kode yang agresif:

ALT_TEXT_HERE

Mari kita visualisasi dampak yang dapat ditimbulkan.

Kami menganalisis waktu pemuatan situs seluler populer dan Progressive Web App menggunakan Statistik Panggilan Runtime V8. Seperti yang dapat kita lihat, waktu penguraian (ditampilkan dalam warna oranye) adalah bagian yang signifikan dari tempat banyak situs ini menghabiskan waktu:

ALT_TEXT_HERE

Wego, situs yang menggunakan PRPL, berhasil mempertahankan waktu penguraian yang rendah untuk rutenya, sehingga menjadi interaktif dengan sangat cepat. Banyak situs lain di atas yang mengadopsi pemisahan kode dan anggaran performa untuk mencoba menurunkan biaya JS mereka.

Bootstrap Progresif

Banyak situs mengoptimalkan visibilitas konten dengan mengorbankan interaktivitas. Untuk mendapatkan first paint yang cepat saat Anda memiliki paket JavaScript yang besar, developer terkadang menggunakan rendering sisi server; lalu "mengupgrade"nya untuk melampirkan pengendali peristiwa saat JavaScript akhirnya diambil.

Berhati-hatilah — hal ini memiliki biayanya sendiri. Anda 1) umumnya mengirimkan respons HTML yang lebih besar yang dapat mendorong interaktivitas, 2) dapat membuat pengguna berada dalam lembah aneh, yaitu setengah pengalaman tidak dapat benar-benar interaktif hingga JavaScript selesai diproses.

Bootstrapping Progresif mungkin merupakan pendekatan yang lebih baik. Kirim halaman yang berfungsi minimal (hanya terdiri dari HTML/JS/CSS yang diperlukan untuk rute saat ini). Seiring dengan semakin banyaknya resource yang tiba, aplikasi dapat melakukan pemuatan lambat dan membuka lebih banyak fitur.

ALT_TEXT_HERE
Progressive Bootstrapping oleh Paul Lewis

Memuat kode yang sebanding dengan apa yang terlihat adalah hal yang sangat penting. PRPL dan Progressive Bootstrapping adalah pola yang dapat membantu mencapai hal ini.

Kesimpulan

Ukuran transmisi sangat penting untuk jaringan kelas bawah. Waktu penguraian penting untuk perangkat yang terikat CPU. Penting untuk menjaga agar hal-hal ini tetap rendah.

Tim telah berhasil menerapkan anggaran performa yang ketat untuk menjaga waktu transmisi dan penguraian/kompilasi JavaScript tetap rendah. Lihat "Can You Afford It?: Real-world Web Performance Budgets" untuk mendapatkan panduan tentang anggaran untuk perangkat seluler.

ALT_TEXT_HERE
Sebaiknya pertimbangkan berapa banyak "headroom" JS yang dapat dihasilkan oleh keputusan arsitektur yang kita buat untuk logika aplikasi.

Jika Anda membuat situs yang menargetkan perangkat seluler, lakukan yang terbaik untuk mengembangkannya di hardware yang representatif, pertahankan waktu penguraian/kompilasi JavaScript Anda tetap rendah, dan terapkan Anggaran Performa untuk memastikan tim Anda dapat memantau biaya JavaScript mereka.

Pelajari Lebih Lanjut