Atau, cara mengontrol perilaku impor JavaScript
<script>
<base>
import.meta.resolve()
Proposal ini memungkinkan kontrol atas URL apa yang diambil oleh pernyataan import
JavaScript dan ekspresi import()
. Hal ini memungkinkan "penentu impor kosong", seperti import moment from "moment"
, berfungsi.
Mekanisme untuk melakukan hal ini adalah melalui peta impor yang dapat digunakan untuk mengontrol resolusi penentu modul secara umum. Sebagai contoh pengantar, pertimbangkan kodenya
import moment from "moment" ;
import { partition } from "lodash" ;
Saat ini, lemparan ini, karena penentu telanjang tersebut dicadangkan secara eksplisit. Dengan menyediakan browser dengan peta impor berikut
< script type =" importmap " >
{
"imports" : {
"moment" : "/node_modules/moment/src/moment.js" ,
"lodash" : "/node_modules/lodash-es/lodash.js"
}
}
</ script >
hal di atas akan bertindak seolah-olah Anda telah menulis
import moment from "/node_modules/moment/src/moment.js" ;
import { partition } from "/node_modules/lodash-es/lodash.js" ;
Untuk mengetahui lebih lanjut tentang nilai "importmap"
baru untuk atribut <script>
type=""
, lihat bagian instalasi. Untuk saat ini, kami akan berkonsentrasi pada semantik pemetaan, menunda diskusi instalasi.
Pengembang web yang berpengalaman dengan sistem modul pra-ES2015, seperti CommonJS (baik dalam Node atau dibundel menggunakan webpack/browserify untuk browser), terbiasa mengimpor modul menggunakan sintaks sederhana:
const $ = require ( "jquery" ) ;
const { pluck } = require ( "lodash" ) ;
Diterjemahkan ke dalam bahasa sistem modul bawaan JavaScript, ini akan menjadi
import $ from "jquery" ;
import { pluck } from "lodash" ;
Dalam sistem seperti itu, penentu impor "jquery"
atau "lodash"
dipetakan ke nama file atau URL lengkap. Secara lebih rinci, penentu ini mewakili paket , biasanya didistribusikan pada npm; dengan hanya menentukan nama paket, mereka secara implisit meminta modul utama paket tersebut.
Manfaat utama sistem ini adalah memungkinkan koordinasi yang mudah di seluruh ekosistem. Siapa pun dapat menulis modul dan menyertakan pernyataan import menggunakan nama paket yang terkenal, dan membiarkan runtime Node.js atau alat build-time mereka menerjemahkannya ke dalam file sebenarnya di disk (termasuk memikirkan pertimbangan pembuatan versi).
Saat ini, banyak pengembang web bahkan menggunakan sintaks modul asli JavaScript, tetapi menggabungkannya dengan penentu impor, sehingga membuat kode mereka tidak dapat berjalan di web tanpa modifikasi terlebih dahulu per aplikasi. Kami ingin menyelesaikan masalah tersebut dan memberikan manfaat ini ke web.
Kami menjelaskan fitur peta impor melalui serangkaian contoh.
Seperti disebutkan dalam pendahuluan,
{
"imports" : {
"moment" : " /node_modules/moment/src/moment.js " ,
"lodash" : " /node_modules/lodash-es/lodash.js "
}
}
memberikan dukungan penentu impor telanjang dalam kode JavaScript:
import moment from "moment" ;
import ( "lodash" ) . then ( _ => ... ) ;
Perhatikan bahwa sisi kanan pemetaan (dikenal sebagai "alamat") harus dimulai dengan /
, ../
, atau ./
, atau dapat diurai sebagai URL absolut, untuk mengidentifikasi URL. Dalam kasus alamat mirip URL relatif, alamat tersebut diselesaikan secara relatif terhadap URL dasar peta impor, yaitu URL dasar laman untuk peta impor sebaris, dan URL sumber daya peta impor untuk peta impor eksternal.
Khususnya, URL relatif "telanjang" seperti node_modules/moment/src/moment.js
tidak akan berfungsi di posisi ini, untuk saat ini. Hal ini dilakukan sebagai default yang konservatif, karena di masa mendatang kami mungkin ingin memperbolehkan beberapa peta impor, yang mungkin mengubah arti sisi kanan dengan cara yang secara khusus memengaruhi kasus-kasus sederhana ini.
Merupakan hal yang umum dalam ekosistem JavaScript untuk memiliki sebuah paket (dalam arti npm) yang berisi banyak modul, atau file lain. Untuk kasus seperti itu, kami ingin memetakan awalan di ruang penentu modul, ke awalan lain di ruang URL yang dapat diambil.
Impor peta melakukan hal ini dengan memberikan arti khusus pada kunci penentu yang diakhiri dengan garis miring. Jadi, seperti peta
{
"imports" : {
"moment" : " /node_modules/moment/src/moment.js " ,
"moment/" : " /node_modules/moment/src/ " ,
"lodash" : " /node_modules/lodash-es/lodash.js " ,
"lodash/" : " /node_modules/lodash-es/ "
}
}
akan memungkinkan tidak hanya mengimpor modul utama seperti
import moment from "moment" ;
import _ from "lodash" ;
tetapi juga modul non-utama, misalnya
import localeData from "moment/locale/zh-cn.js" ;
import fp from "lodash/fp.js" ;
Hal ini juga umum terjadi di ekosistem Node.js untuk mengimpor file tanpa menyertakan ekstensi. Kami tidak memiliki kemewahan untuk mencoba beberapa ekstensi file sampai kami menemukan ekstensi yang cocok. Namun, kita dapat meniru hal serupa dengan menggunakan peta impor. Misalnya,
{
"imports" : {
"lodash" : " /node_modules/lodash-es/lodash.js " ,
"lodash/" : " /node_modules/lodash-es/ " ,
"lodash/fp" : " /node_modules/lodash-es/fp.js " ,
}
}
akan mengizinkan tidak hanya import fp from "lodash/fp.js"
, tetapi juga mengizinkan import fp from "lodash/fp"
.
Meskipun contoh ini menunjukkan bagaimana memungkinkan untuk mengizinkan impor tanpa ekstensi dengan peta impor, hal ini belum tentu diinginkan . Melakukan hal ini akan membengkakkan peta impor, dan membuat antarmuka paket menjadi kurang sederhana—baik bagi manusia maupun perkakas.
Penggembungan ini sangat bermasalah jika Anda perlu mengizinkan impor tanpa ekstensi dalam sebuah paket. Dalam hal ini Anda memerlukan entri peta impor untuk setiap file dalam paket, bukan hanya titik masuk tingkat atas. Misalnya, untuk mengizinkan import "./fp"
dari dalam file /node_modules/lodash-es/lodash.js
, Anda memerlukan pemetaan entri impor /node_modules/lodash-es/fp
ke /node_modules/lodash-es/fp.js
. Sekarang bayangkan mengulangi hal ini untuk setiap file yang direferensikan tanpa ekstensi.
Oleh karena itu, kami menyarankan agar Anda berhati-hati saat menerapkan pola seperti ini pada peta impor atau modul penulisan Anda. Ekosistem akan lebih mudah jika kita tidak bergantung pada impor peta untuk memperbaiki ketidakcocokan terkait ekstensi file.
Sebagai bagian dari mengizinkan pemetaan ulang penentu secara umum, impor peta secara khusus mengizinkan pemetaan ulang penentu mirip URL, seperti "https://example.com/foo.mjs"
atau "./bar.mjs"
. Penggunaan praktisnya adalah memetakan hash, namun di sini kami mendemonstrasikan beberapa hash dasar untuk mengomunikasikan konsep tersebut:
{
"imports" : {
"https://www.unpkg.com/vue/dist/vue.runtime.esm.js" : " /node_modules/vue/dist/vue.runtime.esm.js "
}
}
Pemetaan ulang ini memastikan bahwa setiap impor Vue versi unpkg.com (setidaknya pada URL tersebut) akan mengambil yang dari server lokal.
{
"imports" : {
"/app/helpers.mjs" : " /app/helpers/index.mjs "
}
}
Pemetaan ulang ini memastikan bahwa setiap impor seperti URL yang diselesaikan ke /app/helpers.mjs
, termasuk misalnya import "./helpers.mjs"
dari file di dalam /app/
, atau import "../helpers.mjs"
dari file di dalam /app/models
, malah akan memutuskan ke /app/helpers/index.mjs
. Ini mungkin bukan ide yang bagus; alih-alih membuat tipuan yang mengaburkan kode Anda, Anda sebaiknya memperbarui file sumber untuk mengimpor file yang benar. Namun, ini adalah contoh yang berguna untuk menunjukkan kemampuan mengimpor peta.
Pemetaan ulang tersebut juga dapat dilakukan berdasarkan pencocokan awalan, dengan mengakhiri kunci penentu dengan garis miring:
{
"imports" : {
"https://www.unpkg.com/vue/" : " /node_modules/vue/ "
}
}
Versi ini memastikan bahwa pernyataan impor untuk penentu yang dimulai dengan substring "https://www.unpkg.com/vue/"
akan dipetakan ke URL terkait di bawah /node_modules/vue/
.
Secara umum, intinya adalah pemetaan ulang berfungsi sama untuk impor mirip URL seperti pada impor telanjang. Contoh kami sebelumnya mengubah resolusi penentu seperti "lodash"
, dan dengan demikian mengubah arti import "lodash"
. Di sini kita mengubah resolusi penentu seperti "/app/helpers.mjs"
, dan dengan demikian mengubah arti import "/app/helpers.mjs"
.
Perhatikan bahwa varian garis miring pada pemetaan penentu mirip URL ini hanya berfungsi jika penentu mirip URL memiliki skema khusus: misalnya, pemetaan "data:text/": "/foo"
tidak akan berdampak pada arti import "data:text/javascript,console.log('test')"
, tetapi hanya akan berdampak pada import "data:text/"
.
File skrip sering kali diberi hash unik pada nama filenya, untuk meningkatkan kemampuan cache. Lihat diskusi umum tentang teknik ini, atau diskusi yang lebih berfokus pada JavaScript dan webpack.
Dengan grafik modul, teknik ini dapat menimbulkan masalah:
Pertimbangkan grafik modul sederhana, dengan app.mjs
bergantung pada dep.mjs
yang bergantung pada sub-dep.mjs
. Biasanya, jika Anda meningkatkan atau mengubah sub-dep.mjs
, app.mjs
dan dep.mjs
dapat tetap disimpan dalam cache, sehingga hanya perlu mentransfer sub-dep.mjs
baru melalui jaringan.
Sekarang pertimbangkan grafik modul yang sama, menggunakan nama file hash untuk produksi. Di sana kami memiliki proses pembangunan yang menghasilkan app-8e0d62a03.mjs
, dep-16f9d819a.mjs
, dan sub-dep-7be2aa47f.mjs
dari tiga file asli.
Jika kami meningkatkan atau mengubah sub-dep.mjs
, proses pembangunan kami akan menghasilkan kembali nama file baru untuk versi produksi, misalnya sub-dep-5f47101dc.mjs
. Namun ini berarti kita perlu mengubah pernyataan import
di versi produksi dep.mjs
. Itu mengubah isinya, yang berarti versi produksi dep.mjs
sendiri memerlukan nama file baru. Namun ini berarti kita perlu memperbarui pernyataan import
di versi produksi app.mjs
...
Artinya, dengan grafik modul dan pernyataan import
yang berisi file skrip nama file hash, pembaruan pada bagian mana pun dari grafik menjadi viral bagi semua dependensinya, sehingga kehilangan semua manfaat kemampuan cache.
Impor peta memberikan jalan keluar dari dilema ini, dengan memisahkan penentu modul yang muncul dalam pernyataan import
dari URL di server. Misalnya, situs kita dapat memulai dengan mengimpor peta seperti
{
"imports" : {
"/js/app.mjs" : " /js/app-8e0d62a03.mjs " ,
"/js/dep.mjs" : " /js/dep-16f9d819a.mjs " ,
"/js/sub-dep.mjs" : " /js/sub-dep-7be2aa47f.mjs "
}
}
dan dengan pernyataan import dalam bentuk import "./sub-dep.mjs"
bukan import "./sub-dep-7be2aa47f.mjs"
. Sekarang, jika kita mengubah sub-dep.mjs
, kita cukup memperbarui peta impor kita:
{
"imports" : {
"/js/app.mjs" : " /js/app-8e0d62a03.mjs " ,
"/js/dep.mjs" : " /js/dep-16f9d819a.mjs " ,
"/js/sub-dep.mjs" : " /js/sub-dep-5f47101dc.mjs "
}
}
dan biarkan pernyataan import "./sub-dep.mjs"
saja. Ini berarti konten dep.mjs
tidak berubah, sehingga tetap disimpan dalam cache; sama untuk app.mjs
.
<script>
Catatan penting tentang penggunaan peta impor untuk mengubah arti penentu impor adalah bahwa hal ini tidak mengubah arti URL mentah, seperti yang muncul di <script src="">
atau <link rel="modulepreload">
. Yaitu, mengingat contoh di atas, sementara
import "./app.mjs" ;
akan memetakan ulang dengan benar ke versi hashnya di browser yang mendukung peta impor,
< script type =" module " src =" ./app.mjs " > </ script >
tidak akan: di semua kelas browser, ia akan mencoba mengambil app.mjs
secara langsung, menghasilkan 404. Apa yang akan berhasil, di browser yang mendukung peta impor, adalah
< script type =" module " > import "./app.mjs" ; </ script >
Sering kali Anda ingin menggunakan penentu impor yang sama untuk merujuk ke beberapa versi perpustakaan tunggal, bergantung pada siapa yang mengimpornya. Ini merangkum versi setiap ketergantungan yang digunakan, dan menghindari ketergantungan (postingan blog yang lebih panjang).
Kami mendukung kasus penggunaan ini dalam mengimpor peta dengan memungkinkan Anda mengubah arti penentu dalam cakupan tertentu :
{
"imports" : {
"querystringify" : " /node_modules/querystringify/index.js "
},
"scopes" : {
"/node_modules/socksjs-client/" : {
"querystringify" : " /node_modules/socksjs-client/querystringify/index.js "
}
}
}
(Contoh ini adalah salah satu dari beberapa contoh beberapa versi per aplikasi yang disediakan oleh @zkat. Terima kasih, @zkat!)
Dengan pemetaan ini, di dalam modul apa pun yang URL-nya dimulai dengan /node_modules/socksjs-client/
, penentu "querystringify"
akan merujuk ke /node_modules/socksjs-client/querystringify/index.js
. Sedangkan sebaliknya, pemetaan tingkat atas akan memastikan bahwa "querystringify"
mengacu pada /node_modules/querystringify/index.js
.
Perhatikan bahwa berada dalam cakupan tidak mengubah cara penyelesaian alamat; URL dasar peta impor masih digunakan, misalnya awalan URL cakupan.
Cakupan "mewarisi" satu sama lain dengan cara yang sengaja dibuat sederhana, digabungkan namun dikesampingkan seiring berjalannya waktu. Misalnya, peta impor berikut:
{
"imports" : {
"a" : " /a-1.mjs " ,
"b" : " /b-1.mjs " ,
"c" : " /c-1.mjs "
},
"scopes" : {
"/scope2/" : {
"a" : " /a-2.mjs "
},
"/scope2/scope3/" : {
"b" : " /b-3.mjs "
}
}
}
akan memberikan resolusi sebagai berikut:
Penentu | Perujuk | URL yang dihasilkan |
---|---|---|
A | /scope1/foo.mjs | /a-1.mjs |
B | /scope1/foo.mjs | /b-1.mjs |
C | /scope1/foo.mjs | /c-1.mjs |
A | /scope2/foo.mjs | /a-2.mjs |
B | /scope2/foo.mjs | /b-1.mjs |
C | /scope2/foo.mjs | /c-1.mjs |
A | /scope2/scope3/foo.mjs | /a-2.mjs |
B | /scope2/scope3/foo.mjs | /b-3.mjs |
C | /scope2/scope3/foo.mjs | /c-1.mjs |
Anda dapat memasang peta impor untuk aplikasi Anda menggunakan elemen <script>
, baik inline atau dengan atribut src=""
:
< script type =" importmap " >
{
"imports" : { ... } ,
"scopes" : { ... }
}
</ script >
< script type =" importmap " src =" import-map.importmap " > </ script >
Jika atribut src=""
digunakan, respons HTTP yang dihasilkan harus bertipe MIME application/importmap+json
. (Mengapa tidak menggunakan kembali application/json
? Hal ini dapat mengaktifkan bypass CSP.) Seperti skrip modul, permintaan dibuat dengan CORS diaktifkan, dan respons selalu ditafsirkan sebagai UTF-8.
Karena peta tersebut memengaruhi semua impor, peta impor apa pun harus ada dan berhasil diambil sebelum resolusi modul apa pun dilakukan. Ini berarti pengambilan grafik modul diblokir pada pengambilan peta impor.
Artinya, peta impor dalam bentuk sebaris sangat disarankan untuk performa terbaik. Ini mirip dengan praktik terbaik dalam menyejajarkan CSS penting; kedua jenis sumber daya ini memblokir aplikasi Anda dari melakukan pekerjaan penting sampai mereka diproses, jadi memperkenalkan jaringan bolak-balik kedua (atau bahkan bolak-balik disk-cache) adalah ide yang buruk. Jika Anda ingin menggunakan peta impor eksternal, Anda dapat mencoba mengurangi penalti bolak-balik ini dengan teknologi seperti HTTP/2 Push atau paket pertukaran HTTP.
Sebagai konsekuensi lain dari pengaruh peta impor terhadap semua impor, upaya menambahkan <script type="importmap">
baru setelah pengambilan grafik modul dimulai adalah sebuah kesalahan. Peta impor akan diabaikan, dan elemen <script>
akan memicu peristiwa error
.
Untuk saat ini, hanya satu <script type="importmap">
yang diperbolehkan di halaman. Kami berencana untuk memperluasnya di masa mendatang, setelah kami menemukan semantik yang tepat untuk menggabungkan beberapa peta impor. Lihat pembahasan di #14, #137, dan #167.
Apa yang kita lakukan pada pekerja? Mungkin new Worker(someURL, { type: "module", importMap: ... })
? Atau haruskah Anda mengaturnya dari dalam pekerja? Haruskah pekerja berdedikasi menggunakan peta dokumen pengendali mereka, baik secara default atau selalu? Diskusikan di #2.
Aturan di atas berarti Anda dapat membuat peta impor secara dinamis, selama Anda melakukannya sebelum melakukan impor apa pun. Misalnya:
< script >
const im = document . createElement ( 'script' ) ;
im . type = 'importmap' ;
im . textContent = JSON . stringify ( {
imports : {
'my-library' : Math . random ( ) > 0.5 ? '/my-awesome-library.mjs' : '/my-rad-library.mjs'
}
} ) ;
document . currentScript . after ( im ) ;
</ script >
< script type =" module " >
import 'my-library' ; // will fetch the randomly-chosen URL
</ script >
Contoh yang lebih realistis mungkin menggunakan kemampuan ini untuk menyusun peta impor berdasarkan deteksi fitur:
< script >
const importMap = {
imports : {
moment : '/moment.mjs' ,
lodash : someFeatureDetection ( ) ?
'/lodash.mjs' :
'/lodash-legacy-browsers.mjs'
}
} ;
const im = document . createElement ( 'script' ) ;
im . type = 'importmap' ;
im . textContent = JSON . stringify ( importMap ) ;
document . currentScript . after ( im ) ;
</ script >
< script type =" module " >
import _ from "lodash" ; // will fetch the right URL for this browser
</ script >
Perhatikan bahwa (seperti elemen <script>
lainnya) mengubah konten <script type="importmap">
setelah dimasukkan ke dalam dokumen tidak akan berfungsi. Inilah sebabnya kami menulis contoh di atas dengan merakit konten peta impor sebelum membuat dan memasukkan <script type="importmap">
.
Impor peta adalah hal tingkat aplikasi, seperti pekerja layanan. (Secara lebih formal, peta tersebut akan berupa peta per-modul, dan dengan demikian per-ranah.) Peta tersebut tidak dimaksudkan untuk disusun, melainkan diproduksi oleh manusia atau alat dengan pandangan holistik terhadap aplikasi web Anda. Misalnya, tidak masuk akal jika perpustakaan menyertakan peta impor; perpustakaan dapat dengan mudah mereferensikan modul berdasarkan penentu, dan membiarkan aplikasi memutuskan URL mana yang dipetakan oleh penentu tersebut.
Hal ini, selain kesederhanaan umum, adalah bagian yang memotivasi pembatasan <script type="importmap">
di atas.
Karena peta impor aplikasi mengubah algoritme resolusi untuk setiap modul di peta modul, hal tersebut tidak terpengaruh oleh apakah teks sumber modul berasal dari URL lintas asal. Jika Anda memuat modul dari CDN yang menggunakan penentu impor kosong, Anda harus mengetahui terlebih dahulu penentu impor apa yang ditambahkan modul ke aplikasi Anda, dan memasukkannya ke dalam peta impor aplikasi Anda. (Artinya, Anda perlu mengetahui semua dependensi transitif aplikasi Anda.) Kontrol URL mana yang digunakan untuk setiap paket harus tetap berada di tangan pembuat aplikasi, sehingga mereka dapat mengelola pembuatan versi dan berbagi modul secara holistik.
Sebagian besar browser memiliki parser HTML spekulatif yang mencoba menemukan sumber daya yang dideklarasikan dalam markup HTML sementara parser HTML menunggu skrip pemblokiran diambil dan dieksekusi. Hal ini belum ditentukan, meskipun ada upaya berkelanjutan untuk melakukannya di whatwg/html#5959. Bagian ini membahas beberapa interaksi potensial yang perlu diperhatikan.
Pertama, perhatikan bahwa meskipun sepengetahuan kami tidak ada browser yang melakukan hal tersebut saat ini, parser spekulatif dapat mengambil https://example.com/foo.mjs
dalam contoh berikut, sambil menunggu skrip pemblokiran https://example.com/blocking-1.js
:
<!DOCTYPE html >
<!-- This file is https://example.com/ -->
< script src =" blocking-1.js " > </ script >
< script type =" module " >
import "./foo.mjs" ;
</ script >
Demikian pula, browser dapat mengambil secara spekulatif https://example.com/foo.mjs
dan https://example.com/bar.mjs
dalam contoh berikut, dengan mengurai peta impor sebagai bagian dari proses penguraian spekulatif:
<!DOCTYPE html >
<!-- This file is https://example.com/ -->
< script src =" blocking-2.js " > </ script >
< script type =" importmap " >
{
"imports" : {
"foo" : "./foo.mjs" ,
"https://other.example/bar.mjs" : "./bar.mjs"
}
}
</ script >
< script type =" module " >
import "foo" ;
import "https://other.example/bar.mjs" ;
</ script >
Salah satu interaksi yang perlu diperhatikan di sini adalah bahwa browser yang secara spekulatif mengurai modul JS inline, namun tidak mendukung impor peta, mungkin akan berspekulasi secara salah untuk contoh ini: mereka mungkin secara spekulatif mengambil https://other.example/bar.mjs
, bukannya https://example.com/bar.mjs
itu dipetakan ke dalamnya.
Secara umum, spekulasi impor berbasis peta dapat mengalami kesalahan yang sama seperti spekulasi lainnya. Misalnya jika isi dari blocking-1.js
adalah
const el = document . createElement ( "base" ) ;
el . href = "/subdirectory/" ;
document . currentScript . after ( el ) ;
maka pengambilan spekulatif https://example.com/foo.mjs
dalam contoh peta tanpa impor akan sia-sia, karena ketika tiba waktunya untuk melakukan evaluasi modul yang sebenarnya, kami akan menghitung ulang penentu relatif "./foo.mjs"
dan sadari bahwa yang sebenarnya diminta adalah https://example.com/subdirectory/foo.mjs
.
Begitu pula untuk kasus import map, jika isinya adalah blocking-2.js
document . write ( `<script type="importmap">
{
"imports": {
"foo": "./other-foo.mjs",
"https://other.example/bar.mjs": "./other-bar.mjs"
}
}
</script>` ) ;
maka pengambilan spekulatif https://example.com/foo.mjs
dan https://example.com/bar.mjs
akan sia-sia, karena peta impor yang baru ditulis akan berlaku, bukan yang terlihat sebaris dalam HTML.
<base>
Ketika elemen <base>
ada dalam dokumen, semua URL dan penentu mirip URL di peta impor dikonversi menjadi URL absolut menggunakan href
dari <base>
.
< base href =" https://www.unpkg.com/vue/dist/ " >
< script type =" importmap " >
{
"imports" : {
"vue" : "./vue.runtime.esm.js" ,
}
}
</ script >
< script >
import ( "vue" ) ; // resolves to https://www.unpkg.com/vue/dist/vue.runtime.esm.js
</ script >
Jika browser mendukung metode support(type) HTMLScriptElement, HTMLScriptElement.supports('importmap')
harus mengembalikan nilai true.
if ( HTMLScriptElement . supports && HTMLScriptElement . supports ( 'importmap' ) ) {
console . log ( 'Your browser supports import maps.' ) ;
}
Berbeda dengan Node.js, di browser kita tidak memiliki kemewahan sistem file yang cukup cepat yang dapat kita jelajahi untuk mencari modul. Oleh karena itu, kita tidak dapat mengimplementasikan algoritma resolusi modul Node secara langsung; hal ini memerlukan beberapa kali perjalanan bolak-balik server untuk setiap pernyataan import
, membuang-buang bandwidth dan waktu karena kami terus mendapatkan 404. Kita perlu memastikan bahwa setiap pernyataan import
hanya menyebabkan satu permintaan HTTP; ini memerlukan beberapa ukuran perhitungan awal.
Beberapa orang menyarankan untuk menyesuaikan algoritme resolusi modul browser menggunakan kait JavaScript untuk menafsirkan setiap penentu modul.
Sayangnya, hal ini berakibat fatal bagi kinerja; melompat masuk dan keluar dari JavaScript untuk setiap tepi grafik modul secara drastis memperlambat startup aplikasi. (Aplikasi web pada umumnya memiliki ribuan modul, dengan 3-4× pernyataan import sebanyak itu.) Anda dapat membayangkan berbagai mitigasi, seperti membatasi panggilan hanya pada penentu impor kosong atau mengharuskan hook mengambil kumpulan penentu dan mengembalikan kumpulan URL, tetapi pada akhirnya tidak ada yang mengalahkan perhitungan awal.
Masalah lainnya adalah sulit membayangkan algoritme pemetaan berguna yang dapat ditulis oleh pengembang web, bahkan jika mereka diberikan pengarahan ini. Node.js memilikinya, tetapi ini didasarkan pada perayapan sistem file berulang kali dan memeriksa apakah file ada; kita seperti yang dibahas di atas, itu tidak mungkin dilakukan di web. Satu-satunya situasi di mana algoritma umum akan layak dilakukan adalah jika (a) Anda tidak pernah memerlukan penyesuaian per subgraf, yaitu hanya satu versi dari setiap modul yang ada dalam aplikasi Anda; (b) perkakas berhasil mengatur modul Anda sebelumnya dengan cara yang seragam dan dapat diprediksi, sehingga misalnya algoritme menjadi "return /js/${specifier}.js
". Namun jika kita berada di dunia ini, solusi deklaratif akan lebih sederhana.
Salah satu solusi yang digunakan saat ini (misalnya dalam CDN unpkg melalui babel-plugin-unpkg) adalah menulis ulang semua penentu impor ke URL absolut yang sesuai terlebih dahulu, menggunakan alat build. Hal ini juga dapat dilakukan pada waktu instalasi, sehingga ketika Anda menginstal sebuah paket menggunakan npm, paket tersebut secara otomatis menulis ulang konten paket untuk menggunakan URL absolut atau relatif, bukan penentu impor biasa.
Masalah dengan pendekatan ini adalah pendekatan ini tidak berfungsi dengan import()
dinamis, karena tidak mungkin menganalisis string yang diteruskan ke fungsi tersebut secara statis. Anda dapat memasukkan perbaikan yang, misalnya, mengubah setiap instance import(x)
menjadi import(specifierToURL(x, import.meta.url))
, dengan specifierToURL
adalah fungsi lain yang dihasilkan oleh alat build. Namun pada akhirnya ini adalah abstraksi yang cukup bocor, dan fungsi specifierToURL
sebagian besar menduplikasi pekerjaan proposal ini.
Pada pandangan pertama, pekerja layanan sepertinya merupakan tempat yang tepat untuk melakukan penerjemahan sumber daya semacam ini. Kita telah membicarakan sebelumnya tentang menemukan cara untuk meneruskan penentu bersama dengan peristiwa pengambilan pekerja layanan, sehingga memungkinkannya memberikan kembali Response
yang sesuai.
Namun, pekerja layanan tidak tersedia pada pemuatan pertama . Oleh karena itu, mereka tidak dapat menjadi bagian dari infrastruktur penting yang digunakan untuk memuat modul. Mereka hanya dapat digunakan sebagai penyempurnaan progresif selain pengambilan yang biasanya akan berfungsi.
Jika Anda memiliki aplikasi sederhana yang tidak memerlukan resolusi ketergantungan tercakup, dan memiliki alat instalasi paket yang nyaman untuk menulis ulang jalur pada disk di dalam paket (tidak seperti versi npm saat ini), Anda dapat menggunakan pemetaan yang lebih sederhana. Misalnya, jika alat instalasi Anda membuat daftar formulir datar
node_modules_flattened/
lodash/
index.js
core.js
fp.js
moment/
index.js
html-to-dom/
index.js
maka satu-satunya informasi yang Anda butuhkan adalah
/node_modules_flattened/
)index.js
)Anda dapat membayangkan format konfigurasi impor modul yang hanya menentukan hal-hal ini, atau bahkan hanya beberapa subset (jika kita membuat asumsi untuk yang lain).
Ide ini tidak berlaku untuk aplikasi yang lebih kompleks yang memerlukan resolusi terbatas, jadi kami yakin proposal impor peta lengkap diperlukan. Namun tetap menarik untuk aplikasi sederhana, dan kami bertanya-tanya apakah ada cara untuk membuat proposal juga memiliki mode mudah yang tidak memerlukan daftar semua modul, namun bergantung pada konvensi dan alat untuk memastikan pemetaan minimal yang diperlukan. Diskusikan di #7.
Beberapa kali muncul keinginan orang untuk menyediakan metadata untuk setiap modul; misalnya, metadata integritas, atau opsi pengambilan. Meskipun beberapa orang telah mengusulkan melakukan hal ini dengan pernyataan import, pertimbangan yang cermat terhadap opsi-opsi tersebut mengarah pada pemilihan file manifes out-of-band.
Peta impor bisa berupa file manifes itu. Namun, ini mungkin bukan pilihan terbaik karena beberapa alasan:
Seperti yang dibayangkan saat ini, sebagian besar modul dalam aplikasi tidak memiliki entri di peta impor. Kasus penggunaan utamanya adalah untuk modul yang perlu Anda rujuk dengan penentu telanjang, atau modul di mana Anda perlu melakukan sesuatu yang rumit seperti polyfilling atau virtualisasi. Jika kami membayangkan setiap modul ada di peta, kami tidak akan menyertakan fitur kemudahan seperti paket-via-garis miring.
Semua metadata yang diusulkan sejauh ini berlaku untuk semua jenis sumber daya, bukan hanya modul JavaScript. Sebuah solusi mungkin harus bekerja pada tingkat yang lebih umum.
Wajar jika beberapa <script type="importmap">
muncul di halaman, sama seperti beberapa <script>
jenis lainnya. Kami ingin mengaktifkan ini di masa depan.
Tantangan terbesar di sini adalah menentukan cara menyusun beberapa peta impor. Artinya, dengan adanya dua peta impor yang keduanya memetakan ulang URL yang sama, atau dua definisi cakupan yang mencakup ruang awalan URL yang sama, apa pengaruhnya terhadap laman? Kandidat utama saat ini adalah resolusi berjenjang, yang mengubah peta impor dari penentu impor → pemetaan URL, menjadi serangkaian penentu impor → pemetaan penentu impor, yang pada akhirnya mencapai posisi terbawah dalam "penspesifikasi impor yang dapat diambil" (pada dasarnya adalah URL).
Lihat terbitan terbuka ini untuk diskusi lebih lanjut.
Beberapa kasus penggunaan menginginkan cara membaca atau memanipulasi peta impor suatu wilayah dari skrip, alih-alih melalui penyisipan elemen <script type="importmap">
deklaratif. Anggap saja sebagai "model objek peta impor", mirip dengan model objek CSS yang memungkinkan seseorang memanipulasi aturan CSS halaman yang biasanya bersifat deklaratif.
Tantangannya adalah bagaimana merekonsiliasi peta impor deklaratif dengan perubahan terprogram apa pun, serta kapan API tersebut dapat beroperasi dalam siklus hidup laman. Secara umum, desain yang lebih sederhana kurang kuat dan mungkin menghadapi lebih sedikit kasus penggunaan.
Lihat masalah terbuka ini untuk diskusi lebih lanjut dan kasus penggunaan di mana API terprogram dapat membantu.
import.meta.resolve()
Fungsi import.meta.resolve(specifier)
yang diusulkan memungkinkan skrip modul untuk menyelesaikan penentu impor ke URL kapan saja. Lihat apawg/html#5572 untuk lebih lanjut. Ini terkait dengan impor peta karena memungkinkan Anda menyelesaikan sumber daya "relatif terhadap paket", misalnya
const url = import . meta . resolve ( "somepackage/resource.json" ) ;
akan memberi Anda lokasi resource.json
yang dipetakan dengan tepat dalam namespace somepackage/
yang dikontrol oleh peta impor laman.
Beberapa anggota komunitas telah mengerjakan polyfill dan peralatan terkait peta impor. Inilah yang kami ketahui:
package.json
dan node_modules/
Anda.package.json
.<script type="systemjs-importmap">
.Jangan ragu untuk mengirim permintaan tarik dengan lebih banyak lagi! Selain itu, Anda dapat menggunakan #146 di pelacak isu untuk berdiskusi tentang ruang ini.
Dokumen ini berasal dari sprint sehari penuh yang melibatkan @domenic, @hiroshige-g, @justinfagnani, @MylesBorins, dan @nyaxt. Sejak itu, @guybedford berperan penting dalam pembuatan prototipe dan mendorong diskusi mengenai proposal ini.
Terima kasih juga kepada semua kontributor pada pelacak isu atas bantuan mereka dalam mengembangkan proposal!