Pengoptimalan JavaScript Start-up

Addy Osmani
Addy Osmani

Karena kami membuat situs yang sangat bergantung pada JavaScript, terkadang kami membayar apa yang kami hasilkan dengan cara yang tidak selalu dapat kami lihat dengan mudah. Dalam artikel ini, kami akan membahas mengapa sedikit disiplin dapat membantu jika Anda ingin situs Anda dimuat dan menjadi interaktif dengan cepat di perangkat seluler. Mengirimkan lebih sedikit JavaScript dapat berarti lebih sedikit waktu dalam transmisi jaringan, lebih sedikit kode dekompresi yang dihabiskan, dan lebih sedikit waktu untuk mem-parsing dan mengompilasi JavaScript ini.

Jaringan

Ketika sebagian besar developer memikirkan biaya JavaScript, mereka memikirkannya dari segi biaya download dan eksekusi. Mengirim lebih banyak byte JavaScript melalui kabel membutuhkan waktu lebih lama, sehingga kecepatan koneksi pengguna menjadi lebih lambat.

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

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

Anda dapat mengurangi biaya transfer jaringan JavaScript melalui:

  • Hanya kirimkan kode yang diperlukan pengguna.
    • Gunakan pemisahan kode untuk membagi JavaScript Anda menjadi beberapa yang penting dan mana yang tidak. Pemaket modul seperti webpack mendukung pemisahan kode.
    • Pemuatan lambat dalam kode yang tidak penting.
  • Minifikasi
  • Kompresi
    • Setidaknya, gunakan gzip untuk mengompresi resource berbasis teks.
    • Pertimbangkan untuk menggunakan Brotli ~q11. Pada rasio kompresi, Brotli memiliki performa yang lebih baik dibandingkan gzip. Solusi 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 merespons cache secara efektif. Tentukan masa aktif optimal untuk skrip (usia maksimum) dan berikan token validasi (ETag) untuk menghindari transfer byte yang tidak berubah.
    • Cache Service Worker dapat membuat jaringan aplikasi Anda menjadi lebih tangguh dan memberi Anda akses cepat ke fitur seperti cache kode V8.
    • Gunakan penyimpanan cache jangka panjang untuk menghindari keharusan mengambil kembali resource yang belum berubah. Jika menggunakan Webpack, lihat hashing nama file.

Urai/Kompilasi

Setelah didownload, salah satu biaya terberat JavaScript adalah waktu bagi mesin JS untuk mengurai/mengompilasi kode ini. Di Chrome DevTools, parse dan kompilasi adalah bagian dari waktu "Scripting" berwarna kuning di panel Performa.

ALT_TEXT_HERE

Tab Bottom-Up dan Call Tree menunjukkan 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 pada fase-fase seperti Parse dan Kompilasi

Namun, mengapa hal ini penting?

ALT_TEXT_HERE

Menghabiskan waktu yang lama untuk mengurai/mengompilasi kode dapat sangat memperlambat seberapa cepat pengguna dapat berinteraksi dengan situs Anda. Makin banyak JavaScript yang Anda kirim, makin lama waktu yang diperlukan untuk mengurai dan mengompilasinya sebelum situs Anda menjadi interaktif.

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

Dibandingkan dengan JavaScript, ada banyak biaya yang terlibat dalam pemrosesan gambar yang berukuran setara (masih harus didekode). Namun pada rata-rata hardware seluler, JS lebih cenderung berdampak negatif terhadap 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 dirasterkan. Namun, JS dapat menunda interaktivitas karena biaya penguraian, kompilasi, dan eksekusi.

Ketika berbicara tentang penguraian dan kompilasi yang lambat; konteks itu penting — yang kami maksud adalah rata-rata ponsel di sini. Rata-rata pengguna dapat memiliki ponsel dengan CPU dan GPU lambat, tanpa cache L2/L3, dan yang bahkan mungkin memiliki memori.

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

Di bawah ini, kita dapat melihat biaya penguraian ~1 MB JavaScript yang didekompresi (sederhana) pada hardware yang rendah dan canggih. Ada perbedaan 2–5x dalam waktu untuk mengurai/mengompilasi kode antara ponsel tercepat di pasar dan ponsel biasa.

ALT_TEXT_HERE
Grafik ini menyoroti waktu penguraian untuk paket JavaScript berukuran 1 MB (~250 KB di-gzip) di desktop dan perangkat seluler pada berbagai class. Saat melihat biaya parse, angka terdekompresi yang perlu dipertimbangkan, misalnya ~250KB JS dengan gzip, akan didekompresi menjadi ~1 MB kode.

Bagaimana dengan situs dunia nyata, seperti CNN.com?

Pada iPhone 8 kelas atas, hanya diperlukan ~4 detik untuk mengurai/mengompilasi JS CNN dibandingkan dengan ~13 detik untuk ponsel rata-rata (Moto G4). Hal ini dapat sangat memengaruhi seberapa cepat pengguna dapat berinteraksi sepenuhnya dengan situs ini.

ALT_TEXT_HERE
Di atas, kita melihat waktu penguraian yang membandingkan performa chip A11 Bionic Apple dengan Snapdragon 617 pada hardware Android pada umumnya.

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

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

Apakah kita benar-benar mengeluarkan terlalu banyak JavaScript? Mungkin :)

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

ALT_TEXT_HERE

Perhitungkan waktu yang diperlukan untuk mengambil dan memproses JS serta resource lainnya dan mungkin tidak mengejutkan bahwa pengguna dapat menunggu beberapa saat sebelum merasa halaman siap digunakan. Kita pasti dapat melakukan yang lebih baik di sini.

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

Waktu eksekusi

Ini bukan 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 memengaruhi seberapa cepat pengguna dapat berinteraksi dengan situs Anda.

ALT_TEXT_HERE

Jika skrip dijalankan selama lebih dari 50 milidetik, waktu interaktif akan tertunda selama seluruh jumlah waktu yang diperlukan untuk mendownload, mengompilasi, dan mengeksekusi JS — Alex Russell

Untuk mengatasinya, JavaScript memanfaatkan potongan kecil untuk menghindari penguncian thread utama. Pelajari apakah Anda dapat mengurangi jumlah pekerjaan yang dilakukan selama eksekusi.

Biaya lain

JavaScript dapat memengaruhi performa halaman dengan cara lain:

  • Memori. Halaman dapat terlihat tersendat atau sering dijeda karena GC (pembersihan sampah memori). Saat browser mengklaim kembali memori, eksekusi JS akan dijeda sehingga browser yang sering membersihkan sampah memori dapat menjeda eksekusi lebih sering daripada yang mungkin kita inginkan. Hindari kebocoran memori dan jeda GC yang sering agar halaman tetap bebas jank.
  • Selama runtime, JavaScript yang berjalan lama dapat memblokir thread utama yang menyebabkan halaman tidak responsif. Membagi pekerjaan menjadi potongan-potongan 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 untuk mempertahankan waktu penguraian/kompilasi dan waktu pengiriman jaringan untuk JavaScript dengan lambat, ada pola yang dapat membantu seperti pemotongan berbasis rute atau PRPL.

PRPL

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

ALT_TEXT_HERE

Mari kita visualisasikan dampaknya.

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 penting dari waktu sebagian besar situs tersebut menghabiskan waktu:

ALT_TEXT_HERE

Wego, situs yang menggunakan PRPL, mengelola waktu penguraian yang rendah untuk rute mereka, sehingga mendapatkan interaktif dengan sangat cepat. Banyak situs lain di atas menerapkan anggaran pemisahan kode dan performa untuk mencoba menurunkan biaya JS.

Bootstrap Progresif

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

Hati-hati — hal ini memiliki biaya sendiri. Anda 1) umumnya mengirimkan respons HTML lebih besar yang dapat mendorong interaktivitas kami, 2) dapat membuat pengguna berada di lembah aneh, di mana separuh pengalaman tidak dapat benar-benar interaktif hingga JavaScript selesai diproses.

Bootstrap Progresif mungkin merupakan pendekatan yang lebih baik. Kirimkan halaman yang berfungsi secara minimal (yang hanya terdiri dari HTML/JS/CSS yang dibutuhkan untuk rute saat ini). Seiring meningkatnya jumlah resource, aplikasi dapat mengalami pemuatan lambat dan membuka lebih banyak fitur.

ALT_TEXT_HERE
Bootstrapping Progresif oleh Paul Lewis

Kode pemuatan yang proporsional dengan tampilan adalah objek yang tidak disangka-sangka. PRPL dan {i>Bootstrapping<i} Progresif adalah pola yang dapat membantu mencapai hal ini.

Kesimpulan

Ukuran transmisi sangat penting untuk jaringan kelas bawah. Waktu penguraian penting untuk perangkat yang mengandalkan CPU. Pertahankan tingkat kepercayaan yang rendah ini penting.

Tim telah menemukan keberhasilan dalam mengadopsi anggaran performa yang ketat untuk menjaga agar waktu transmisi JavaScript dan penguraian/kompilasi tetap rendah. Lihat tulisan Alex Russell "Can You Afford It?: Anggaran Performa Web Sesungguhnya" untuk panduan anggaran untuk seluler.

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

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

Pelajari Lebih Lanjut