JavaScript pemisahan kode

Memuat resource JavaScript yang besar akan memengaruhi kecepatan halaman secara signifikan. Membagi JavaScript Anda menjadi potongan-potongan yang lebih kecil dan hanya mengunduh apa yang diperlukan untuk agar halaman berfungsi selama startup dapat meningkatkan pemuatan halaman secara signifikan responsivitas, sehingga dapat meningkatkan Interaksi ke Berikutnya di halaman Anda Paint (INP).

Saat laman mengunduh, mengurai, dan mengompilasi file JavaScript berukuran besar, hal itu bisa menjadi tidak responsif selama periode waktu tertentu. Elemen halaman terlihat, karena merupakan bagian dari HTML awal halaman dan diberi gaya oleh CSS. Namun, karena JavaScript yang diperlukan untuk menjalankan elemen-elemen interaktif tersebut—serta skrip lain yang dimuat halaman—mungkin mengurai dan mengeksekusi JavaScript agar mereka dapat berfungsi. Tujuan hasilnya adalah bahwa pengguna mungkin merasa seolah-olah interaksi itu signifikan tertunda, atau bahkan rusak sama sekali.

Hal ini sering terjadi karena thread utama diblokir, ketika JavaScript diuraikan dan dikompilasi di thread utama. Jika proses ini memakan waktu terlalu lama, elemen halaman mungkin tidak cukup cepat merespons input pengguna. Satu solusi untuk hal ini adalah dengan memuat hanya JavaScript yang Anda butuhkan agar halaman berfungsi, sementara menangguhkan JavaScript lain untuk dimuat nanti melalui teknik yang dikenal sebagai pemisahan. Modul ini berfokus pada cara yang terakhir dari kedua teknik tersebut.

Mengurangi penguraian dan eksekusi JavaScript selama startup melalui pemisahan kode

Lighthouse menampilkan peringatan jika eksekusi JavaScript membutuhkan waktu lebih dari 2 detik, dan gagal jika memerlukan waktu lebih dari 3,5 detik. JavaScript yang Berlebihan penguraian dan eksekusi berpotensi menjadi masalah mana saja di halaman siklus proses, karena berpotensi meningkatkan penundaan input interaksi jika waktu saat pengguna berinteraksi dengan halaman bertepatan dengan tugas thread utama yang bertanggung jawab untuk memproses dan mengeksekusi JavaScript sedang berjalan.

Lebih dari itu, eksekusi dan penguraian JavaScript yang berlebihan sangat bermasalah saat pemuatan awal halaman, karena ini adalah titik di halaman siklus proses di mana pengguna cenderung berinteraksi dengan halaman tersebut. Bahkan, Total Blocking Time (TBT)—metrik responsivitas pemuatan—berkorelasi yang sangat tinggi dengan INP, yang menunjukkan bahwa pengguna memiliki kecenderungan tinggi untuk mencoba interaksi selama pemuatan halaman awal.

Audit Lighthouse yang melaporkan waktu yang dihabiskan untuk menjalankan setiap file JavaScript permintaan halaman Anda berguna karena dapat membantu Anda mengidentifikasi dengan tepat skrip mungkin menjadi kandidat untuk pemisahan kode. Anda kemudian dapat melangkah lebih jauh dengan menggunakan alat cakupan di Chrome DevTools untuk mengidentifikasi secara tepat bagian mana dari {i>JavaScript <i}halaman tidak akan digunakan selama pemuatan halaman.

Pemisahan kode adalah teknik bermanfaat yang dapat mengurangi JavaScript awal halaman payload. Ini memungkinkan Anda membagi paket JavaScript menjadi dua bagian:

  • JavaScript yang diperlukan saat halaman dimuat, sehingga tidak dapat dimuat di baik.
  • Sisa JavaScript yang dapat dimuat di lain waktu, paling sering pada titik di mana pengguna berinteraksi dengan elemen interaktif yang diberikan di pada halaman.

Pemisahan kode dapat dilakukan menggunakan sintaksis import() dinamis. Ini sintaksis—tidak seperti elemen <script> yang meminta resource JavaScript tertentu saat startup—membuat permintaan untuk resource JavaScript di lain waktu selama siklus proses halaman.

document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
  // Get the form validation named export from the module through destructuring:
  const { validateForm } = await import('/validate-form.mjs');

  // Validate the form:
  validateForm();
}, { once: true });

Dalam cuplikan JavaScript sebelumnya, modul validate-form.mjs didownload, diuraikan, dan dieksekusi hanya saat pengguna memburamkan salah satu formulir Kolom <input>. Dalam situasi ini, sumber daya JavaScript yang bertanggung jawab untuk mengarahkan logika validasi formulir hanya akan melibatkan halaman ketika yang paling mungkin digunakan.

Pemaket JavaScript seperti webpack, Parcel, Rollup, dan esbuild dapat dikonfigurasi untuk membagi paket JavaScript menjadi potongan-potongan yang lebih kecil setiap kali temukan panggilan import() dinamis dalam kode sumber Anda. Sebagian besar {i>tool<i} ini melakukan ini secara otomatis, tetapi esbuild secara khusus mengharuskan Anda untuk ikut serta pengoptimalan.

Catatan bermanfaat tentang pemisahan kode

Meskipun pemisahan kode adalah metode efektif untuk mengurangi pertentangan thread utama selama pemuatan laman awal, ada baiknya untuk mengingat beberapa hal jika Anda memutuskan untuk mengaudit kode sumber JavaScript Anda guna mendapatkan peluang pemisahan kode.

Gunakan pemaket jika Anda bisa

Sudah menjadi praktik umum bagi developer untuk menggunakan modul JavaScript selama proses pengembangan Anda. Ini adalah peningkatan pengalaman developer yang luar biasa yang meningkatkan keterbacaan dan pengelolaan kode. Namun, ada beberapa karakteristik performa yang kurang optimal yang dapat dihasilkan saat mengirimkan JavaScript modul ke production.

Yang paling penting, Anda harus menggunakan pemaket untuk memproses dan mengoptimalkan sumber daya , termasuk modul yang ingin Anda pisahkan kodenya. Bundler sangat efektif dalam tidak hanya menerapkan pengoptimalan ke kode sumber JavaScript, tetapi juga cukup efektif dalam menyeimbangkan pertimbangan performa seperti ukuran paket terhadap rasio kompresi. Efektivitas kompresi meningkat sesuai ukuran paket, tetapi pemaket juga mencoba memastikan bahwa paket tidak terlalu besar sehingga dikenakan tugas yang berjalan lama karena evaluasi skrip.

Pemaket juga menghindari masalah pengiriman modul yang tidak dipaketkan dalam jumlah besar melalui jaringan. Arsitektur yang menggunakan modul JavaScript cenderung memiliki struktur hierarki modul yang kompleks. Jika hierarki modul tidak dipaket, setiap modul merepresentasikan permintaan HTTP terpisah, dan interaktivitas di aplikasi web Anda mungkin tertunda jika Anda tidak memaketkan modul. Meskipun Anda dapat menggunakan Petunjuk resource <link rel="modulepreload"> untuk memuat hierarki modul besar sedini sebanyak mungkin, paket JavaScript masih lebih disukai daripada performa pemuatan dari sudut pandang yang lebih luas.

Jangan menonaktifkan kompilasi streaming secara tidak sengaja

Mesin JavaScript V8 Chromium menawarkan sejumlah pengoptimalan yang siap pakai untuk memastikan kode JavaScript produksi Anda dimuat seefisien mungkin. Salah satu pengoptimalan ini dikenal sebagai kompilasi streaming yang—seperti penguraian bertahap HTML yang di-streaming ke browser—mengompilasi potongan kode yang di-streaming saat JavaScript tiba dari jaringan.

Anda memiliki beberapa cara untuk memastikan bahwa kompilasi streaming terjadi untuk aplikasi web di Chromium:

  • Ubah kode produksi Anda untuk menghindari penggunaan modul JavaScript. Bundle dapat mengubah kode sumber JavaScript Anda berdasarkan target kompilasi, dan target sering kali bersifat spesifik untuk lingkungan tertentu. V8 akan menerapkan streaming kompilasi ke kode JavaScript apa pun yang tidak menggunakan modul, dan Anda bisa mengonfigurasi pemaket Anda untuk mengubah kode modul JavaScript menjadi sintaksis yang tidak menggunakan modul JavaScript dan fitur-fiturnya.
  • Jika Anda ingin mengirimkan modul JavaScript ke produksi, gunakan .mjs ekstensi. Baik JavaScript produksi Anda menggunakan modul maupun tidak, ada tidak ada jenis konten khusus untuk JavaScript yang menggunakan modul versus JavaScript sementara yang tidak. Jika V8 khawatir, Anda secara efektif memilih untuk tidak ikut streaming kompilasi saat Anda mengirimkan modul JavaScript dalam lingkungan production menggunakan .js . Jika Anda menggunakan ekstensi .mjs untuk modul JavaScript, V8 dapat memastikan kompilasi streaming untuk kode JavaScript berbasis modul tidak rusak.

Jangan biarkan pertimbangan ini menghalangi Anda menggunakan pemisahan kode. Kode adalah cara efektif untuk mengurangi {i>payload<i} JavaScript awal kepada pengguna, tetapi dengan menggunakan bundler dan mengetahui cara mempertahankan streaming V8 perilaku kompilasi, Anda dapat memastikan bahwa kode JavaScript produksi Anda cepat bagi pengguna.

Demo impor dinamis

paket web

webpack dilengkapi dengan plugin bernama SplitChunksPlugin, yang memungkinkan Anda mengonfigurasi cara pemaket membagi file JavaScript. webpack mengenali pernyataan import() dinamis dan import statis. Perilaku SplitChunksPlugin dapat diubah dengan menentukan opsi chunks di konfigurasi:

  • chunks: async adalah nilai default dan merujuk pada panggilan import() dinamis.
  • chunks: initial merujuk ke panggilan import statis.
  • chunks: all mencakup impor import() dinamis dan statis, sehingga Anda dapat untuk berbagi potongan antara impor async dan initial.

Secara default, setiap kali webpack menemukan pernyataan import() dinamis. ini membuat potongan terpisah untuk modul tersebut:

/* main.js */

// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';

myFunction('Hello world!');

// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
  // Assumes top-level await is available. More info:
  // https://v8.dev/features/top-level-await
  await import('/form-validation.js');
}

Konfigurasi webpack default untuk cuplikan kode sebelumnya menghasilkan dua potongan yang terpisah:

  • Potongan main.js—yang diklasifikasikan webpack sebagai potongan initial—yang mencakup modul main.js dan ./my-function.js.
  • Potongan async, yang hanya mencakup form-validation.js (berisi hash file dalam nama resource jika dikonfigurasi). Potongan ini hanya didownload jika dan saat condition benar.

Konfigurasi ini memungkinkan Anda menunda pemuatan potongan form-validation.js hingga yang sebenarnya dibutuhkan. Hal ini dapat meningkatkan responsivitas pemuatan dengan mengurangi skrip evaluasi selama pemuatan halaman awal. Download dan evaluasi skrip untuk potongan form-validation.js terjadi saat kondisi tertentu terpenuhi, dalam hal ini, modul yang diimpor secara dinamis akan diunduh. Salah satu contohnya adalah kondisi di mana polyfill hanya diunduh untuk browser tertentu, atau—seperti pada contoh sebelumnya—modul yang diimpor diperlukan untuk interaksi pengguna.

Di sisi lain, mengubah konfigurasi SplitChunksPlugin untuk menentukan chunks: initial memastikan bahwa kode hanya dibagi pada potongan awal. Berikut adalah potongan seperti yang diimpor secara statis, atau tercantum dalam entry webpack properti. Melihat contoh sebelumnya, potongan yang dihasilkan adalah kombinasi form-validation.js dan main.js dalam satu file skrip, yang mengakibatkan performa pemuatan halaman awal yang lebih buruk.

Opsi untuk SplitChunksPlugin juga dapat dikonfigurasi untuk memisahkan skrip menjadi beberapa skrip yang lebih kecil—misalnya dengan menggunakan opsi maxSize untuk menginstruksikan webpack agar membagi potongan menjadi file terpisah jika potongan tersebut melebihi apa yang ditentukan oleh maxSize. Membagi file skrip besar menjadi file yang lebih kecil dapat meningkatkan responsivitas beban, seperti dalam beberapa kasus, evaluasi skrip yang menggunakan CPU secara intensif pekerjaan dibagi menjadi tugas-tugas yang lebih kecil, yang cenderung tidak menghambat untuk jangka waktu yang lebih lama.

Selain itu, membuat file JavaScript yang lebih besar juga berarti bahwa skrip kemungkinan besar akan terjadi pembatalan validasi cache. Misalnya, jika Anda mengirimkan skrip besar dengan kerangka kerja dan kode aplikasi pihak pertama, seluruh paket dapat dibatalkan jika hanya framework yang diupdate, tetapi tidak ada lagi resource yang dipaketkan.

Di sisi lain, file skrip yang lebih kecil meningkatkan kemungkinan bahwa pengunjung mengambil sumber daya dari cache, sehingga pemuatan halaman menjadi lebih cepat kunjungan berulang. Namun, file yang lebih kecil mendapat manfaat yang lebih sedikit dari kompresi dibandingkan dengan file yang lebih besar satu, dan dapat meningkatkan waktu round-trip jaringan pada pemuatan halaman dengan dalam cache browser. Harus diperhatikan untuk mencapai keseimbangan antara penyimpanan dalam cache efisiensi, efektivitas kompresi, dan waktu evaluasi skrip.

demo webpack

demo SplitChunksPlugin webpack.

Menguji pengetahuan Anda

Jenis pernyataan import mana yang digunakan saat menjalankan kode memisahkan?

import() dinamis.
Benar.
import statis.
Coba lagi.

Jenis pernyataan import manakah yang harus berada di bagian atas modul JavaScript, dan tidak ada di lokasi lain?

import() dinamis.
Coba lagi.
import statis.
Benar.

Saat menggunakan SplitChunksPlugin di webpack, apa perbedaan antara potongan async dan initial potongan?

async potongan dimuat menggunakan import() dinamis dan potongan initial dimuat menggunakan import.
Benar.
async potongan dimuat menggunakan import statis dan potongan initial dimuat menggunakan import().
Coba lagi.

Berikutnya: Gambar pemuatan lambat dan elemen <iframe>

Meskipun ini cenderung menjadi jenis sumber daya yang cukup mahal, JavaScript bukanlah satu-satunya jenis sumber daya yang bisa Anda tunda pemuatannya. Gambar dan elemen <iframe> berpotensi menimbulkan biaya mahal. Mirip dengan JavaScript, Anda dapat dapat menunda pemuatan gambar dan elemen <iframe> dengan pemuatan lambat mereka, seperti yang dijelaskan di modul berikutnya dalam kursus ini.