Melewati kode yang tepat untuk setiap browser bukanlah tugas mudah.
Pada artikel ini, kami akan mempertimbangkan beberapa opsi untuk mengatasi masalah ini.

Melewati kode modern oleh browser modern dapat sangat meningkatkan kinerja. Paket JavaScript Anda akan dapat berisi sintaksis modern yang lebih ringkas atau dioptimalkan dan mendukung browser lama.
Di antara alat untuk pengembang, pola
modul / nomodule memuat deklaratif modern atau kode lama mendominasi, yang menyediakan browser dengan sumber dan memungkinkan Anda untuk memutuskan mana yang akan digunakan:
<script type="module" src="/modern.js"></script> <script nomodule src="/legacy.js"></script>
Sayangnya, tidak semuanya begitu sederhana. Pendekatan HTML yang ditunjukkan di atas memicu
reload skrip di Edge dan Safari .
Apa yang bisa dilakukan?
Bergantung pada peramban, kami perlu memberikan salah satu opsi untuk skrip yang dikompilasi, tetapi beberapa peramban lama tidak mendukung semua sintaks yang diperlukan untuk ini.
Pertama, ada
Perbaikan Safari . Safari 10.1 mendukung modul JS, bukan atribut
nomodule
dalam skrip, yang memungkinkannya untuk mengeksekusi kode modern dan lama. Namun, acara sebelum-
beforeload
non-standar yang didukung oleh Safari 10 & 11 dapat digunakan untuk polyfill
nomodule
.
Metode Satu: Unduhan Dinamis
Anda dapat mengatasi masalah ini dengan menerapkan pemuat skrip kecil. Mirip dengan cara kerja
LoadCSS . Alih-alih berharap untuk implementasi modul-ES dan atribut
nomodule
di
nomodule
, Anda dapat mencoba untuk mengeksekusi skrip modul sebagai "tes dengan tes lakmus", dan berdasarkan hasilnya, pilih untuk mengunduh kode modern atau lawas.
<!-- use a module script to detect modern browsers: --> <script type="module"> self.modern = true </script> <!-- now use that flag to load modern VS legacy code: --> <script> addEventListener('load', function() { var s = document.createElement('script') if ('noModule' in s) { </script>
Tetapi dengan pendekatan ini, Anda harus menunggu skrip modul "lakmus" selesai sebelum Anda menerapkan skrip yang benar. Ini terjadi karena
<sript type="module">
selalu bekerja secara tidak sinkron. Tapi ada cara yang lebih baik!
Anda dapat menerapkan opsi independen dengan memeriksa apakah
nomodule
di browser. Ini berarti bahwa kami akan mempertimbangkan browser seperti Safari 10.1 sebagai usang, bahkan jika mereka mendukung modul. Tapi itu
bisa menjadi yang
terbaik . Ini kode yang relevan:
var s = document.createElement('script') if ('noModule' in s) {
Ini dapat dengan cepat diubah menjadi fungsi yang memuat kode modern atau lawas, dan juga menyediakan pemuatan asinkron:
<script> $loadjs("/modern.js","/legacy.js") function $loadjs(src,fallback,s) { s = document.createElement('script') if ('noModule' in s) s.type = 'module', s.src = src else s.async = true, s.src = fallback document.head.appendChild(s) } </script>
Apa kompromi di sini?
PreloadKarena solusinya benar-benar dinamis, browser tidak akan dapat mendeteksi sumber daya JavaScript kami sampai ia memulai kode bootstrap yang kami tulis untuk memasukkan skrip modern atau lawas. Biasanya, browser memindai HTML untuk mencari sumber yang dapat diunduh sebelumnya. Masalah ini terpecahkan, tetapi tidak secara ideal: Anda dapat memuat versi modern paket di browser modern menggunakan
<link rl=modulpreload>
.
Sayangnya, sejauh ini
hanya Chrome yang mendukung modulepreload
.
<link rel="modulepreload" href="/modern.js"> <script type="module">self.modern=1</script> <!-- etc -->
Jika teknik ini cocok untuk Anda, Anda bisa mengurangi ukuran dokumen HTML yang menjadi tempat Anda menyematkan skrip ini. Jika payload Anda kecil, seperti layar splash atau kode unduhan aplikasi klien, maka menjatuhkan preload scanner tidak akan mempengaruhi kinerja. Dan jika Anda menggambar banyak HTML penting di server untuk dikirim ke browser, maka pemindai preload akan berguna bagi Anda dan pendekatan yang dijelaskan tidak akan menjadi pilihan terbaik untuk Anda.
Inilah yang terlihat seperti solusi ini digunakan:
<link rel="modulepreload" href="/modern.js"> <script type="module">self.modern=1</script> <script> $loadjs("/modern.js","/legacy.js") function $loadjs(e,d,c){c=document.createElement("script"),self.modern?(c.src=e,c.type="module"):c.src=d,document.head.appendChild(c)} </script>
Perlu juga dicatat bahwa daftar browser yang mendukung modul JS hampir sama dengan yang mendukung
<link rl=preload>
. Untuk beberapa situs, mungkin pantas menggunakan
<link rl=preload as=script crossorigin>
sebagai ganti
modulepreload
. Performa dapat menurun karena preloading skrip klasik tidak menyiratkan penguraian yang seragam dari waktu ke waktu, seperti halnya dengan
modulepreload
.
Metode Dua: Lacak Agen Pengguna
Saya tidak memiliki contoh kode yang cocok karena melacak Agen Pengguna adalah tugas yang tidak sepele. Tetapi kemudian Anda dapat membaca
artikel yang sangat bagus
di Smashing Magazine.Faktanya, semuanya dimulai dengan
<scrit src=bundle.js>
di HTML untuk semua browser. Ketika bundle.js diminta, server mem-parsing string Agen Pengguna dari browser yang meminta dan memilih JavaScript yang akan dikembalikan - modern atau lawas, tergantung pada bagaimana browser dikenali.
Pendekatan ini bersifat universal, tetapi menimbulkan konsekuensi serius:
- Karena server pintar diperlukan, pendekatan ini tidak akan berfungsi dalam kondisi penggunaan statis (generator situs statis, Netlify, dll.).
- Caching untuk URL JavaScript ini sekarang tergantung pada Agen Pengguna, yang sangat tidak stabil.
- Definisi UA sulit dan dapat menyebabkan klasifikasi yang salah.
- Baris User Agent mudah dipalsukan, dan UA baru muncul setiap hari.
Salah satu cara mengatasi keterbatasan ini adalah menggabungkan pola modul / nomodule dengan diferensiasi Agen Pengguna untuk menghindari pengiriman beberapa versi paket ke alamat yang sama. Pendekatan ini mengurangi cache halaman, tetapi menyediakan preloading yang efisien: server penghasil HTML tahu kapan harus menggunakan
modulepreload
dan kapan harus
preload
.
function renderPage(request, response) { let html = `<html><head>...`; const agent = request.headers.userAgent; const isModern = userAgent.isModern(agent); if (isModern) { html += ` <link rel="modulepreload" href="modern.mjs"> <script type="module" src="modern.mjs"></script> `; } else { html += ` <link rel="preload" as="script" href="legacy.js"> <script src="legacy.js"></script> `; } response.end(html); }
Untuk situs yang sudah menghasilkan HTML di server sebagai tanggapan atas setiap permintaan, ini bisa menjadi transisi yang efektif untuk mengunduh skrip modern.
Metode tiga: browser lama yang bagus
Efek negatif dari pola modul / nomodule terlihat di versi Chrome, Firefox, dan Safari yang lebih lama - jumlahnya sangat kecil, karena peramban diperbarui secara otomatis. Dengan Edge 16-18, situasinya berbeda, tetapi ada harapan: Edge versi baru akan menggunakan mesin rendering berbasis Chromium, yang tidak memiliki masalah seperti itu.
Untuk beberapa aplikasi, ini akan menjadi kompromi yang ideal: unduh kode versi modern di 90% browser, dan berikan kode lama kepada yang lama. Muatan di browser yang lebih lama akan meningkat.
Ngomong-ngomong, tidak ada Agen Pengguna yang rebootnya merupakan masalah tidak menempati pangsa pasar ponsel yang signifikan. Jadi sumber dari semua byte tambahan ini tidak mungkin berupa perangkat seluler atau perangkat dengan prosesor yang lemah.
Jika Anda membuat situs yang terutama diakses oleh peramban seluler atau peramban baru, maka bagi sebagian besar pengguna ini, jenis modul / pola nomodule yang paling sederhana cocok. Pastikan Anda menambahkan
perbaikan Safari 10.1 jika perangkat iOS yang lebih lama mendatangi Anda.
iOS-. <!-- polyfill `nomodule` in Safari 10.1: --> <script type="module"> !function(e,t,n){!("noModule"in(t=e.createElement("script")))&&"onbeforeload"in t&&(n=!1,e.addEventListener("beforeload",function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()},!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove())}(document) </script> <!-- 90+% of browsers: --> <script src="modern.js" type="module'></script> <!-- IE, Edge <16, Safari <10.1, old desktop: --> <script src="legacy.js" nomodule async defer></script>
Metode Empat: Terapkan Ketentuan Paket
Solusi yang baik adalah menggunakan
nomodule
untuk mengunduh paket dengan kode yang tidak diperlukan di browser modern, seperti polyfill. Dengan pendekatan ini, dalam kasus terburuk, polyfill akan dimuat atau bahkan dieksekusi (di Safari 10.1), tetapi efeknya akan terbatas pada “re-polyfilling”. Mengingat hari ini pendekatan dengan mengunduh dan mengeksekusi polyfill di semua browser berlaku, ini bisa menjadi peningkatan yang layak.
<!-- newer browsers will not load this bundle: --> <script nomodule src="polyfills.js"></script> <!-- all browsers load this one: --> <script src="/bundle.js"></script>
Anda dapat mengkonfigurasi CLI Angular untuk menggunakan pendekatan ini dengan polyfill, seperti yang diperlihatkan
Minko Gachev. Setelah mempelajari tentang pendekatan ini, saya menyadari bahwa Anda dapat mengaktifkan injeksi polyfill otomatis di preact-cli -
PR ini menunjukkan betapa mudahnya menerapkan teknik ini.
Dan jika Anda menggunakan WebPack, maka ada
plugin yang nyaman untuk
html-webpack-plugin
, yang membuatnya mudah untuk menambahkan nomodule ke paket dengan polyfill.
Jadi apa yang harus dipilih?
Jawabannya tergantung pada situasi Anda. Jika Anda membuat aplikasi klien, dan HTML Anda mengandung lebih dari
<sript>
, maka Anda mungkin perlu
metode pertama .
Jika Anda membuat situs yang dirender di server, dan Anda dapat melakukan caching, maka
metode kedua mungkin cocok
untuk Anda .
Jika Anda menggunakan render
universal , perolehan kinerja yang ditawarkan oleh pemindaian pra-muat bisa sangat penting. Karena itu, perhatikan metode
ketiga atau
keempat . Pilih yang cocok dengan arsitektur Anda.
Secara pribadi, saya memilih, dengan fokus pada durasi penguraian pada perangkat seluler, dan bukan pada biaya pengunduhan dalam versi desktop. Pengguna seluler menganggap biaya penguraian dan transfer data sebagai biaya aktual (konsumsi baterai dan biaya transfer data), sedangkan pengguna desktop tidak memiliki batasan seperti itu. Saya juga melanjutkan dari pengoptimalan untuk 90% pengguna - pemirsa utama proyek saya menggunakan browser modern dan / atau mobile.
Apa yang harus dibaca
Ingin mempelajari lebih lanjut tentang topik ini? Anda bisa mulai dari sini: