Menggunakan modul JavaScript dalam produksi: keadaan saat ini. Bagian 2

Hari ini kami menerbitkan bagian kedua dari terjemahan materi, yang dikhususkan untuk penggunaan JS-modul dalam produksi.



→ Ngomong-ngomong, ini bagian pertama artikel.

Impor dinamis


Salah satu kelemahan menggunakan ekspresi impor nyata untuk memisahkan kode dan memuat modul adalah bahwa tugas bekerja dengan browser yang tidak mendukung modul ada pada pengembang.

Dan jika Anda ingin menggunakan perintah import() dinamis import() untuk mengatur pemuatan kode malas, maka Anda, antara lain, harus berurusan dengan fakta bahwa beberapa browser, walaupun, yang paling pasti, modul dukungan , masih tidak mendukung perintah impor dinamis () (Tepi 16–18, Firefox 60–66, Safari 11, Chrome 61–63).

Untungnya, masalah ini akan membantu kita menyelesaikan polyfill kecil (sekitar 400 byte) dan sangat cepat.

Menambahkan polyfill ini ke proyek web sangat sederhana. Anda hanya perlu mengimpor dan menginisialisasi di titik entri utama ke aplikasi (sebelum memanggil perintah import() dalam kode):

 import dynamicImportPolyfill from 'dynamic-import-polyfill'; //         .   //    ,       . dynamicImportPolyfill.initialize({modulePath: '/modules/'}); 

Dan hal terakhir yang perlu dilakukan agar skema ini bekerja, adalah memberi tahu Rollup bahwa ia perlu mengganti nama perintah import() dinamis yang muncul dalam kode menggunakan nama yang Anda pilih (melalui opsi output.dynamicImportFunction ). Sebuah polyfill yang mengimplementasikan fitur impor dinamis menggunakan nama __import__ secara default, tetapi dapat dikustomisasi .

Alasan Anda perlu mengganti nama import() adalah karena import adalah, dalam JavaScript, kata kunci. Ini berarti bahwa tidak mungkin, dengan cara polyfill, untuk mengatur penggantian perintah import() standar import() dengan perintah dengan nama yang sama. Jika Anda mencoba melakukan ini, kesalahan sintaksis akan terjadi.

Tetapi sangat bagus bahwa Rollup mengganti nama perintah selama pembangunan proyek, karena ini berarti Anda dapat menggunakan perintah standar dalam kode sumber. Selain itu, di masa mendatang, ketika polyfill tidak lagi diperlukan, kode sumber proyek tidak perlu ditulis ulang, mengubah untuk import apa yang sebelumnya disebut entah bagaimana berbeda.

Pemuatan JavaScript yang efisien


Setiap kali Anda menggunakan pemisahan kode, tidak ada salahnya mengatur preloading semua modul yang Anda tahu akan dimuat segera (misalnya, ini semua modul di pohon dependensi dari modul utama, yang merupakan titik masuk ke proyek).

Tetapi ketika kita memuat modul JavaScript nyata (melalui <script type="module"> dan kemudian melalui perintah import sesuai), kita harus menggunakan atribut modulepreload alih-alih preload yang biasa, yang hanya ditujukan untuk skrip klasik.

 <link rel="modulepreload" href="/modules/main.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-one.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-two.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-three.XXXX.mjs"> <!-- ... --> <script type="module" src="/modules/main.XXXX.mjs"></script> 

Bahkan, modulepreload jelas lebih baik daripada mekanisme preload tradisional dalam preloading modul nyata. Faktanya adalah bahwa ketika Anda menggunakannya, bukan hanya file itu diunduh. Selain itu, segera, di luar utas utama, mulai parsing dan kompilasi skrip. preload reguler tidak dapat melakukan ini karena, selama preload, tidak tahu apakah file akan digunakan sebagai modul atau sebagai skrip biasa.

Ini berarti bahwa memuat modul menggunakan atribut modulepreload seringkali lebih cepat, dan ketika modul diinisialisasi, kecil kemungkinannya untuk membuat beban berlebihan pada utas utama, yang menyebabkan masalah antarmuka.

Membuat daftar modul untuk preloading


Fragmen input dalam objek bundel Rollup berisi daftar lengkap impor di pohon dependensi statis mereka. Akibatnya, di kait Rollup generateBundle , mudah untuk mendapatkan daftar file yang harus dimuat sebelumnya.

Meskipun plugin dapat ditemukan di npm untuk membuat daftar modulepreload, membuat daftar yang serupa untuk setiap titik input di pohon dependensi hanya memerlukan beberapa baris kode. Karena itu, saya lebih suka membuat daftar seperti itu secara manual, menggunakan sesuatu seperti kode ini:

 {  generateBundle(options, bundle) {    //         .    const modulepreloadMap = {};    for (const [fileName, chunkInfo] of Object.entries(bundle)) {      if (chunkInfo.isEntry || chunkInfo.isDynamicEntry) {        modulepreloadMap[chunkInfo.name] = [fileName, ...chunkInfo.imports];      }    }    //  -   ...    console.log(modulepreloadMap);  } } 

Di sini, misalnya, adalah bagaimana saya membuat daftar modulepreload untuk philipwalton.com dan untuk aplikasi demo saya, yang akan kita bahas di bawah ini.

Harap perhatikan bahwa meskipun atribut modulepreload jelas lebih baik daripada preload klasik untuk memuat skrip modul, ia memiliki dukungan browser terburuk (saat ini hanya Chrome yang mendukungnya). Jika sebagian besar lalu lintas Anda tidak berasal dari Chrome, maka dalam situasi Anda mungkin masuk akal untuk menggunakan preload reguler alih-alih preload .

Mengenai penggunaan preload , saya ingin memperingatkan Anda tentang sesuatu. Faktanya adalah bahwa ketika memuat skrip menggunakan preload , tidak seperti modulepreload , skrip ini tidak masuk ke peta modul browser. Ini berarti bahwa ada kemungkinan bahwa permintaan preload dapat dieksekusi lebih dari satu kali (misalnya, jika modul mengimpor file sebelum browser selesai melakukan preloading).

Mengapa menggunakan modul nyata dalam produksi?


Jika Anda sudah menggunakan bundler seperti webpack , serta jika Anda sudah menggunakan pemisahan kode dan preloading file yang sesuai (mirip dengan apa yang saya katakan), maka Anda mungkin bertanya-tanya apakah Anda harus beralih ke strategi berfokus pada penggunaan modul nyata. Ada beberapa alasan yang membuat saya percaya bahwa Anda harus mempertimbangkan beralih ke modul. Selain itu, saya percaya bahwa mengubah proyek menjadi modul nyata lebih baik daripada menggunakan skrip klasik dengan kode mereka sendiri yang dirancang untuk memuat modul.

▍ Mengurangi jumlah total kode


Jika proyek menggunakan modul nyata, maka pengguna browser modern tidak perlu mengunduh beberapa kode tambahan yang dirancang untuk memuat modul atau untuk mengelola dependensi. Misalnya, saat menggunakan modul nyata, Anda tidak perlu memuat mekanisme runtime dan manifes webpack .

▍ Peningkatan kode preload


Seperti yang disebutkan di bagian sebelumnya, menggunakan atribut modulepreload memungkinkan modulepreload untuk memuat kode dan modulepreload - modulepreload dan mengkompilasinya di luar utas utama. Yang lainnya, dibandingkan dengan atribut preload , tetap sama. Ini berarti bahwa berkat modulepreload halaman menjadi lebih cepat interaktif, dan yang mengurangi kemungkinan memblokir aliran utama selama interaksi pengguna.

Akibatnya, terlepas dari ukuran apa kode aplikasi dibagi menjadi beberapa fragmen, akan jauh lebih produktif untuk mengunduh fragmen ini menggunakan perintah impor dan atribut modulepreload daripada memuatnya menggunakan tag script biasa dan atribut preload biasa (terutama jika tag yang sesuai dihasilkan dinamis dan ditambahkan ke DOM saat runtime).

Dengan kata lain, bundel rollup dari beberapa kode proyek, yang terdiri dari 20 fragmen modul, akan memuat lebih cepat dari bundel proyek yang sama, terdiri dari 20 fragmen skrip klasik yang disiapkan oleh webpack (bukan karena menggunakan webpack, tetapi karena bahwa ini bukan modul nyata).

▍ Meningkatkan fokus kode di masa depan


Banyak fitur baru browser yang dibangun berdasarkan modul, dan tidak didasarkan pada skrip klasik. Ini berarti bahwa jika Anda berencana untuk menggunakan fitur-fitur ini, maka kode Anda harus disajikan dalam bentuk modul nyata. Seharusnya bukan sesuatu yang ditranskrip dalam ES5 dan dimuat dengan cara tag script klasik (ini adalah masalah yang saya tulis ketika saya mencoba menggunakan API Penyimpanan KV eksperimental).

Berikut adalah beberapa fitur browser baru yang paling menarik yang berfokus secara eksklusif pada modul:


Dukungan Browser Legacy


Secara global, lebih dari 83% browser mendukung modul JavaScript (termasuk impor dinamis), sebagai akibatnya, sebagian besar pengguna akan dapat bekerja dengan proyek yang beralih ke modul tanpa upaya khusus dari pihak pengembang proyek ini.

Dalam hal browser yang mendukung modul tetapi tidak mendukung impor dinamis, disarankan untuk menggunakan dynamic-import-polyfill polyfill yang dijelaskan di atas. Karena sangat kecil dan, jika mungkin, menggunakan metode import() berbasis browser standar, penggunaan polyfill ini hampir tidak berpengaruh pada ukuran atau kinerja proyek.

Jika kita berbicara tentang browser yang sama sekali tidak mendukung modul, maka, untuk mengatur pekerjaan dengan mereka, Anda dapat menggunakan pola modul / nomodule .

▍ Contoh pengoperasian


Karena selalu lebih mudah untuk berbicara tentang kompatibilitas lintas-browser daripada mencapainya, saya membuat aplikasi demo yang menggunakan teknologi yang dibahas di atas.

Aplikasi ini berfungsi di peramban seperti Edge 18 dan Firefox ESR, yang tidak mendukung perintah import() dinamis import() . Selain itu, ini berfungsi di browser seperti Internet Explorer 11, yang tidak mendukung modul.

Untuk menunjukkan bahwa strategi yang dibahas di sini tidak hanya cocok untuk proyek-proyek sederhana, saya menggunakan banyak fitur dalam aplikasi ini yang dibutuhkan saat ini dalam proyek-proyek besar:

  • Transformasi kode menggunakan Babel (termasuk JSX).
  • Ketergantungan umumJS (mis. Bereaksi dan bereaksi).
  • Dependensi CSS.
  • Hashing Sumber Daya
  • Pemisahan kode
  • Impor dinamis (dengan fallback sebagai polyfill).
  • Implementasi pola modul / nomodule.

Kode proyek dapat ditemukan di GitHub (yaitu, Anda dapat melakukan fork repositori dan membangun proyek sendiri), versi demo dihosting di Glitch , yang memungkinkan Anda untuk bereksperimen dengannya.

Hal terpenting dalam proyek demo adalah konfigurasi Rollup , karena menentukan bagaimana modul yang dihasilkan dibuat.

Ringkasan


Saya harap materi ini meyakinkan Anda tidak hanya tentang kemungkinan penerapan modul JavaScript standar dalam produksi, tetapi juga bahwa itu benar-benar dapat meningkatkan waktu pemuatan situs dan kinerjanya.

Berikut ini adalah ikhtisar singkat tentang langkah-langkah yang perlu Anda ambil untuk mengimplementasikan modul-modul dalam proyek:

  • Gunakan bundler, di antara format output yang didukung oleh modul ES2015.
  • Secara agresif mendekati pemisahan kode (jika mungkin, sampai alokasi dependensi dari node_modules menjadi fragmen yang terpisah).
  • Preload semua modul yang ada di pohon dependensi statis Anda (menggunakan modulepreload ).
  • Gunakan polyfill untuk browser yang tidak mendukung pernyataan import() dinamis import() .
  • Gunakan pola modul / nomodule untuk mengatur pekerjaan dengan browser yang tidak mendukung modul.

Jika Anda sudah menggunakan Rollup untuk membangun proyek, saya ingin Anda mencoba apa yang saya bicarakan di sini dan pergi untuk menggunakan modul nyata dalam produksi (menggunakan pemisahan kode dan teknik impor dinamis). Jika Anda melakukannya, beri tahu saya bagaimana kabar Anda. Saya tertarik mengetahui tentang masalah dan tentang kasus-kasus sukses memperkenalkan modul.

Sangat jelas bahwa modul adalah masa depan JavaScript. Saya ingin melihat, dan sebaiknya segera, bagaimana alat yang kami gunakan dan perpustakaan bantu mengadopsi teknologi ini. Saya harap materi ini setidaknya dapat membantu proses ini sedikit.

Pembaca yang budiman! Apakah Anda menggunakan modul JS dalam produksi?

Source: https://habr.com/ru/post/id466539/


All Articles