Pengoptimalan JavaScript Start-up

Karena kita membangun situs yang sangat bergantung pada JavaScript, menyebabkan kita terkadang membayar apa yang kita keluarkan dengan cara yang tidak selalu bisa kita lihat dengan mudah. Dalam artikel ini, kami akan menjelaskan mengapa sedikit disiplin bisa membantu jika Anda menginginkan situs Anda dimuat dan cepat interaktif di perangkat seluler. Mengirimkan lebih sedikit JavaScript bisa berarti lebih sedikit waktu dalam transmisi jaringan, lebih sedikit kode dekompresi yang dihabiskan, dan lebih sedikit waktu untuk memparsing dan mengompilasi JavaScript ini.

Jaringan

Ketika sebagian besar developer memikirkan tentang biaya JavaScript, mereka memikirkan biaya tersebut sebagai bagian dari biaya download dan eksekusi. Mengirim lebih banyak byte JavaScript melalui kabel membutuhkan waktu lebih lama, sehingga memperlambat koneksi pengguna.

Ketika browser meminta
resource, resource tersebut harus diambil lalu dikompres. Dalam kasus
resource seperti JavaScript, resource tersebut harus di-parse dan dikompilasi sebelum
eksekusi.

Hal ini bisa menjadi masalah, bahkan di negara-negara maju, karena jenis koneksi jaringan efektif yang dimiliki pengguna mungkin sebenarnya bukan 3G, 4G, maupun Wi-Fi. Anda bisa saja menggunakan Wi-Fi di sebuah kafe, tetapi terhubung ke hotspot seluler dengan kecepatan 2G.

Anda bisa mengurangi biaya transfer jaringan JavaScript melalui:

  • Hanya mengirimkan kode yang dibutuhkan oleh pengguna.
    • Gunakan pemisahan-kode untuk membagi JavaScript Anda ke dalam apa yang penting dan apa yang tidak penting. Paket modul seperti dukungan webpack pemisahan-kode.
    • Tidak sering memuat kode yang tidak penting.
  • Minifikasi
  • Kompresi
    • Minimal menggunakan gzip untuk mengompresi resource berbasis teks.
    • Pertimbangkan menggunakan Brotli ~q11. Brotli memiliki kinerja yang lebih baik dibandingkan gzip untuk rasio kompresinya. Brotli membantu CertSimple menghemat 17% on ukuran byte JS yang dikompresi dan membantu LinkedIn menghemat 4% waktu muat mereka.
  • Menghapus kode yang tidak digunakan.
  • Meng-cache kode untuk meminimalkan lalu lintas jaringan.
    • Gunakan HTTP caching guna memastikan bahwa cache browser merespons secara efektif. Tentukan lifetime optimal untuk skrip (usia-maks) dan berikan token validasi (ETag) untuk menghindari transfer byte yang tidak berubah.
    • Caching Service Worker dapat membuat jaringan aplikasi Anda lebih tangguh dan memberi Anda akses cepat ke fitur-fitur seperti Caching kode V8.
    • Gunakan caching jangka panjang untuk menghindari keharusan mengambil kembali sumber daya yang belum berubah. Jika menggunakan Webpack, lihat hashing nama file.

Parse/Kompilasi

Setelah diunduh, salah satu yang paling memberatkan dari JavaScript adalah waktu untuk mesin JS untuk mem-parse/mengompilasi kode ini. Di Chrome DevTools, parse dan kompilasi adalah bagian waktu "Scripting" berwarna kuning pada panel Kinerja.

Tab Bottom-Up dan Call Tree menunjukkan waktu Parse/kompilasi secara tepat:

Chrome panel Kinerja DevTools > Bottom-Up. Dengan mengaktifkan Statistik Panggilan Runtime V8, kita bisa melihat waktu yang dihabiskan pada fase-fase seperti Parse dan Kompilasi

Tetapi, mengapai ini penting?

Menghabiskan waktu yang lama untuk mem-parse/mengompilasi kode bisa sangat memperlambat kecepatan pengguna dalam berinteraksi dengan situs Anda. Semakin banyak JavaScript yang Anda kirim, semakin lama waktu yang dibutuhkan untuk mem-parse mengompilasinya sebelum situs Anda interaktif.

Byte-for-byte, Pemrosesan JavaScript lebih memberatkan browser daripada gambar atau Web Font dengan ukuran setara — Tom Dale

Dibandingkan dengan JavaScript, ada banyak biaya yang terlibat dalam pemrosesan gambar berukuran sama (masih harus dikoding!), tetapi pada sebagian besar perangkat keras seluler, JS lebih cenderung berdampak negatif terhadap interaktivitas halaman.

JavaScript dan byte gambar memiliki biaya yang sangat berbeda. Gambar biasanya tidak menghalangi thread utama atau mengganggu antarmuka agar tidak interaktif saat didekodekan dan dirasterkan. Namun demikian, JS dapat menunda interaktivitas karena biaya parse, kompilasi, dan eksekusi.

Ketika berbicara tentang parse dan kompilasi yang menjadi lambat; konteks itu penting — Kita sedang membicarakan tentang rata-rata ponsel di sini. Rata-rata pengguna bisa memiliki ponsel dengan CPU dan GPU lambat, tanpa cache L2/L3, dan mungkin memori yang sangat terbatas.

Kemampuan jaringan dan kemampuan perangkat tidak selalu selaras. Pengguna dengan koneksi Fiber yang luar biasa tetapi tidak memiliki CPU terbaik untuk mem-parse dan mengevaluasi JavaScript yang dikirimkan ke perangkat mereka. Bisa juga sebaliknya, koneksi jaringan yang buruk, tetapi CPU yang luar biasa cepat. — Kristofer Baxter, LinkedIn

Di bawah ini kita dapat melihat biaya parse ~ 1MB dari JavaScript yang didekompresi (sederhana) pada perangkat keras low-end dan high-end. Ada 2–5x perbedaan waktu untuk mem-parse/mengompilasi kode antara ponsel tercepat di pasaran dengan ponsel rata-rata.

Grafik ini menyoroti waktu parse untuk paket JavaScript 1MB (dikompresi ~250KB dengan gzip) di desktop dan perangkat seluler dari berbagai kelas. Saat melihat biaya parse, angka-angka dekompresi yang perlu dipertimbangkan, mis. ~250KB JS dengan kompresi gzip didekompresi menjadi ~1MB kode.

Bagaimana dalam praktik nyata, seperti CNN.com?

Pada iPhone 8 canggih, dibutuhkan hanya ~4 detik untuk mem-parse/mengompilasi JS CNN dibandingkan dengan ~13 detik untuk ponsel rata-rata (Moto G4). Perbedaan secara signifikan dapat mempengaruhi seberapa cepat pengguna dapat berinteraksi sepenuhnya dengan situs ini.

Di atas kita melihat waktu parse dibandingkan kinerja chip A11 Bionic Apple dibandingkan Snapdragon 617 pada lebih banyak perangkat keras Android.

Ini menyoroti pentingnya pengujian pada perangkat rata-rata (seperti G4), bukan hanya ponsel yang mungkin ada di saku Anda. Bagaimanapun, konteks itu penting: optimalkan sesuai perangkat dan kondisi jaringan yang dimiliki pengguna Anda.

Google Analytics bisa memberikan insight ke dalam kelas perangkat seluler yang digunakan pengguna nyata untuk mengakses situs Anda. Ini bisa memberikan peluang untuk memahami kendala CPU/GPU nyata yang mereka gunakan.

Apakah kita benar-benar mengeluarkan terlalu banyak JavaScript? Emm, mungkin :)

Menggunakan Arsip HTTP (situs ~500K teratas) untuk menganalisis keadaan JavaScript di seluler, kita bisa melihat bahwa 50% situs memerlukan waktu 14 detik untuk interaktif. Situs-situs ini memerlukan hingga 4 detik hanya untuk mem-parse dan mengompilasi JS.

Faktor dalam waktu yang diperlukan untuk mengambil data dan memproses JS serta resource lainnya dan mungkin tidak mengejutkan bahwa pengguna mungkin bisa tetap menunggu beberapa saat sebelum merasakan halaman siap untuk digunakan. Kita bisa melakukan lebih baik di sini.

Menghapus JavaScript yang tidak penting dari halaman Anda dapat mengurangi waktu transmisi, parsi dan kompilasi intensif-CPU, dan potensi overhead memori. Ini juga membantu membuat halaman Anda lebih cepat interaktif.

Waktu eksekusi

Tidak hanya parse dan kompilasi yang memerlukan biaya. Eksekusi JavaScript (menjalankan kode setelah di-parse/dikompilasi) adalah salah satu operasi yang harus dilakukan di thread utama. Waktu eksekusi yang lama juga dapat mempengaruhi seberapa cepat seorang pengguna dapat berinteraksi dengan situs Anda.

Jika skrip dieksekusi lebih dari 50 milidetik, waktu menuju interaktif tertunda oleh seluruh jumlah waktu yang diperlukan untuk mendownload, mengompilasi, dan mengeksekusi JS — Alex Russell

Untuk mengatasinya, JavaScript memanfaatkan potongan-potongan kecil untuk menghindari penguncian thread utama. Gali lebih jauh apakah Anda bisa menurunkan seberapa banyak pekerjaan yang bisa dilakukan selama eksekusi.

Biaya lainnya

JavaScript bisa mempengaruhi kinerja halaman dengan cara lain:

  • Memori. Halaman dapat terlihat tersendat atau sering berhenti karena GC (garbage collection) atau pembersihan sampah memori. Ketika browser mengambil kembali memori, eksekusi JS dijeda sehingga browser yang sering membersihkan sampah memori dapat menjeda eksekusi lebih sering daripada yang kita harapkan. Hindari kebocoran memori dan jeda GC yang terlalu sering agar halaman Anda tidak tersendat.
  • Selama runtime, JavaScript yang berjalan lama dapat memblokir thread utama , menyebabkan halaman yang tidak responsif. Memotong-motong pekerjaan menjadi potongan-potongan kecil (menggunakan requestAnimationFrame() atau requestIdleCallback() untuk penjadwalan) bisa meminimalkan masalah kinerja respons.

Pola untuk menurunkan biaya pengiriman JavaScript

Saat Anda mencoba untuk mempertahankan waktu parse/kompilasi dan waktu transmisi jaringan untuk JavaScript, ada pola yang bisa membantu seperti memotong-motong berdasarkan rute atau PRPL.

PRPL

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

Mari kita visualisasikan dampaknya.

Kami menganalisis waktu muat situs seluler populer dan Aplikasi Web Progresif menggunakan Statistik Panggilan Runtime V8. Seperti yang dapat kita lihat, waktu parse (ditampilkan dalam warna jingga) adalah bagian signifikan yang banyak digunakan oleh situs-situs ini untuk menghabiskan waktu:

Wego, situs yang menggunakan PRPL, mempertahankan waktu parse rendah untuk rute mereka, mendapatkan interaktif dengan sangat cepat. Banyak situs lain di atas mengadopsi anggaran pemecahan-kode dan kinerja untuk mencoba menurunkan biaya JS.

Bootstrapping Progresif

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

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

Bootstrapping Progresif mungkin menjadi pendekatan yang lebih baik. Keluarkan halaman fungsional secara minimal (yang hanya terdiri dari HTML/JS/CSS yang dibutuhkan untuk rute saat ini). Semakin banyaknya resource, aplikasi bisa mengalami lazy-load dan membuka lebih banyak fitur.

Bootstrapping Progresif oleh Paul Lewis

Memuat kode secara proporsional dengan apa yang dilihat adalah yang terbaik. PRPL dan Bootstrapping Progresif adalah pola yang dapat membantu mencapai hal ini.

Kesimpulan

Ukuran transmisi sangat penting untuk jaringan low end. Waktu parse adalah penting untuk perangkat yang mengandalkan CPU. Pertahankan agar selalu rendah.

Tim telah menemukan keberhasilan mengadopsi anggaran kinerja yang ketat untuk menjaga agar waktu transmisi Java Script dan parse/kompilasi tetap rendah. Lihat apa yang disampaikan oleh Alex Russell, "Bisakah Anda Mencapainya?: Anggaran Kinerja Web Dunia Nyata" untuk panduan anggaran untuk perangkat seluler.

Sangat berguna jika Anda mempertimbangkan seberapa banyak "headroom" JS yang bisa dibuat dari keputusan arsitektur kita bisa mempengaruhi logika aplikasi.

Jika Anda sedang membangun situs yang menargetkan perangkat seluler, lakukan yang terbaik untuk mengembangkannya pada perangkat keras yang representatif, jaga waktu parse/kompilasi JavaScript Anda tetap rendah, dan terapkan Anggaran Kinerja untuk memastikan tim Anda dapat memantau biaya JavaScript.

Pelajari Lebih Lanjut