或者,如何控制 JavaScript 導入的行為
<script>
<base>
元素import.meta.resolve()
該提案允許控制 JavaScript import
語句和import()
表達式取得哪些 URL。這允許「裸導入說明符」(例如import moment from "moment"
起作用。
執行此操作的機制是透過導入映射,通常可用於控制模組說明符的解析。作為介紹性範例,請考慮以下程式碼
import moment from "moment" ;
import { partition } from "lodash" ;
今天,這會拋出異常,因為此類裸說明符已被明確保留。透過向瀏覽器提供以下導入映射
< script type =" importmap " >
{
"imports" : {
"moment" : "/node_modules/moment/src/moment.js" ,
"lodash" : "/node_modules/lodash-es/lodash.js"
}
}
</ script >
上面的行為就好像你寫了一樣
import moment from "/node_modules/moment/src/moment.js" ;
import { partition } from "/node_modules/lodash-es/lodash.js" ;
有關<script>
的type=""
屬性的新"importmap"
值的更多信息,請參閱安裝部分。現在,我們將專注於映射的語義,並推遲安裝討論。
具有 ES2015 之前的模組系統經驗的 Web 開發人員,例如 CommonJS(在 Node 中或使用 webpack/browserify 為瀏覽器捆綁),習慣於能夠使用簡單的語法導入模組:
const $ = require ( "jquery" ) ;
const { pluck } = require ( "lodash" ) ;
翻譯成 JavaScript 內建模組系統的語言,這些將是
import $ from "jquery" ;
import { pluck } from "lodash" ;
在這樣的系統中,這些"jquery"
或"lodash"
的裸導入說明符被對應到完整的檔案名稱或 URL。更詳細地說,這些說明符代表packages ,通常分佈在npm上;透過僅指定套件的名稱,它們隱式地請求該套件的主模組。
該系統的主要好處是它可以輕鬆地在整個生態系統中進行協調。任何人都可以編寫模組並使用套件的眾所周知的名稱包含導入語句,並讓 Node.js 運行時或其建置時工具負責將其轉換為磁碟上的實際檔案(包括確定版本控制注意事項)。
如今,許多 Web 開發人員甚至使用 JavaScript 的本機模組語法,但將其與裸露的導入說明符相結合,從而使他們的程式碼無法在每個應用程式的提前修改的情況下在 Web 上運行。我們希望解決這個問題,並將這些好處帶給網路。
我們透過一系列範例來解釋導入映射的功能。
正如簡介中所提到的,
{
"imports" : {
"moment" : " /node_modules/moment/src/moment.js " ,
"lodash" : " /node_modules/lodash-es/lodash.js "
}
}
在 JavaScript 程式碼中提供裸導入說明符支援:
import moment from "moment" ;
import ( "lodash" ) . then ( _ => ... ) ;
請注意,映射的右側(稱為「位址」)必須以/
、 ../
或./
開頭,或可解析為絕對 URL,以標識 URL。對於類似相對 URL 的位址,它們是相對於導入映射的基本 URL 進行解析的,即內聯導入映射的頁面的基本 URL 以及外部導入映射的導入映射資源的 URL。
特別是,像node_modules/moment/src/moment.js
這樣的「裸」相對 URL 目前在這些位置不起作用。這是作為保守的預設值完成的,因為將來我們可能希望允許多個導入映射,這可能會以特別影響這些裸露情況的方式改變右側的含義。
在 JavaScript 生態系中,一個套件(在 npm 的意義上)包含多個模組或其他檔案是很常見的。對於這種情況,我們希望將模組說明符空間中的前綴對應到可取得 URL 空間中的另一個前綴。
導入映射透過為以斜線結尾的說明符鍵賦予特殊含義來實現此目的。因此,像這樣的地圖
{
"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/ "
}
}
不僅允許導入主要模組,例如
import moment from "moment" ;
import _ from "lodash" ;
還有非主模組,例如
import localeData from "moment/locale/zh-cn.js" ;
import fp from "lodash/fp.js" ;
在 Node.js 生態系中導入不包含副檔名的檔案也很常見。在找到合適的匹配之前,我們沒有機會嘗試多個檔案副檔名。然而,我們可以透過使用導入映射來模擬類似的東西。例如,
{
"imports" : {
"lodash" : " /node_modules/lodash-es/lodash.js " ,
"lodash/" : " /node_modules/lodash-es/ " ,
"lodash/fp" : " /node_modules/lodash-es/fp.js " ,
}
}
不僅允許import fp from "lodash/fp.js"
,還允許import fp from "lodash/fp"
。
儘管此範例展示瞭如何允許使用導入映射進行無擴展導入,但這並不一定是可取的。這樣做會使導入映射變得臃腫,並使包的介面變得不那麼簡單——無論是對於人類還是對於工具來說。
如果您需要允許在包內進行無擴展導入,這種膨脹尤其成問題。在這種情況下,您將需要套件中每個檔案的匯入映射條目,而不僅僅是頂級入口點。例如,要允許從/node_modules/lodash-es/lodash.js
檔案import "./fp"
,您需要一個匯入條目將/node_modules/lodash-es/fp
對應到/node_modules/lodash-es/fp.js
。現在想像一下對每個不帶擴展名引用的檔案重複此操作。
因此,我們建議在導入映射或編寫模組中使用此類模式時要小心。如果我們不依賴導入映射來修補與檔案副檔名相關的不匹配,那么生態系統將會變得更簡單。
作為允許一般重新映射說明符的一部分,導入映射特別允許重新映射類似 URL 的說明符,例如"https://example.com/foo.mjs"
或"./bar.mjs"
。其實際用途是映射雜湊值,但在這裡我們演示了一些基本的雜湊值來傳達該概念:
{
"imports" : {
"https://www.unpkg.com/vue/dist/vue.runtime.esm.js" : " /node_modules/vue/dist/vue.runtime.esm.js "
}
}
此重新對應可確保任何 unpkg.com 版本的 Vue 匯入(至少在該 URL 處)都從本機伺服器取得。
{
"imports" : {
"/app/helpers.mjs" : " /app/helpers/index.mjs "
}
}
此重新映射確保解析為/app/helpers.mjs
任何類似 URL 的導入,包括例如從/app/
內的文件import "./helpers.mjs"
,或從文件import "../helpers.mjs"
在/app/models
內,將解析為/app/helpers/index.mjs
。這可能不是一個好主意;您應該更新原始檔案以匯入正確的文件,而不是建立混淆程式碼的間接尋址。但是,它是演示導入地圖功能的有用範例。
這種重新映射也可以在前綴匹配的基礎上完成,透過以尾部斜線結束說明符鍵:
{
"imports" : {
"https://www.unpkg.com/vue/" : " /node_modules/vue/ "
}
}
此版本確保以子字串"https://www.unpkg.com/vue/"
開頭的說明符的導入語句將對應到/node_modules/vue/
下的對應 URL。
一般來說,重點是重新映射對於類似 URL 的導入和裸導入的作用是一樣的。我們先前的範例改變了像"lodash"
這樣的說明符的解析,從而改變了import "lodash"
的含義。在這裡,我們更改了諸如"/app/helpers.mjs"
之類的說明符的分辨率,從而更改了import "/app/helpers.mjs"
的含義。
請注意,這種類似 URL 的說明符映射的尾部斜杠變體僅在類似 URL 的說明符具有特殊方案時才有效:例如, "data:text/": "/foo"
的映射不會影響import "data:text/javascript,console.log('test')"
,但只會影響import "data:text/"
。
腳本檔案的檔案名稱中通常會被賦予唯一的雜湊值,以提高可快取性。請參閱該技術的一般討論,或更多以 JavaScript 和 webpack 為中心的討論。
對於模組圖,這種技術可能會出現問題:
考慮一個簡單的模組圖,其中app.mjs
依賴dep.mjs
,後者又依賴sub-dep.mjs
。通常,如果您升級或更改sub-dep.mjs
, app.mjs
和dep.mjs
可以保留緩存,只需要透過網路傳輸新的sub-dep.mjs
。
現在考慮相同的模組圖,使用雜湊檔案名稱進行生產。在那裡,我們的建置過程從原始的三個檔案產生app-8e0d62a03.mjs
、 dep-16f9d819a.mjs
和sub-dep-7be2aa47f.mjs
。
如果我們升級或更改sub-dep.mjs
,我們的建置流程將為生產版本重新產生一個新檔名,例如sub-dep-5f47101dc.mjs
。但這意味著我們需要更改dep.mjs
生產版本中的import
語句。這會改變它的內容,這意味著dep.mjs
的生產版本本身需要一個新的檔案名稱。但這意味著我們需要更新app.mjs
生產版本中的import
語句......
也就是說,對於包含雜湊檔案名稱腳本檔案的模組圖和import
語句,對圖任何部分的更新都會對其所有依賴項進行病毒式傳播,從而失去所有可緩存性優勢。
導入映射提供了一種擺脫這種困境的方法,它將import
語句中出現的模組說明符與伺服器上的 URL 解耦。例如,我們的網站可以從匯入地圖開始,例如
{
"imports" : {
"/js/app.mjs" : " /js/app-8e0d62a03.mjs " ,
"/js/dep.mjs" : " /js/dep-16f9d819a.mjs " ,
"/js/sub-dep.mjs" : " /js/sub-dep-7be2aa47f.mjs "
}
}
並使用import "./sub-dep.mjs"
形式的導入語句,而不是import "./sub-dep-7be2aa47f.mjs"
。現在,如果我們更改sub-dep.mjs
,我們只需更新導入映射:
{
"imports" : {
"/js/app.mjs" : " /js/app-8e0d62a03.mjs " ,
"/js/dep.mjs" : " /js/dep-16f9d819a.mjs " ,
"/js/sub-dep.mjs" : " /js/sub-dep-5f47101dc.mjs "
}
}
並保留import "./sub-dep.mjs"
語句。這意味著dep.mjs
的內容不會更改,因此它會保持快取狀態; app.mjs
也是如此。
<script>
關於使用導入映射更改導入說明符含義的一個重要事項是,它不會更改原始 URL 的含義,例如<script src="">
或<link rel="modulepreload">
中出現的 URL。也就是說,考慮到上面的例子,同時
import "./app.mjs" ;
將在支援匯入映射的瀏覽器中正確地重新映射到其哈希版本,
< script type =" module " src =" ./app.mjs " > </ script >
不會:在所有類別的瀏覽器中,它都會嘗試直接取得app.mjs
,從而導致 404。
< script type =" module " > import "./app.mjs" ; </ script >
通常情況下,您希望使用相同的匯入說明符來引用單一庫的多個版本,具體取決於匯入它們的人。這封裝了正在使用的每個依賴項的版本,並避免了依賴地獄(較長的部落格文章)。
我們透過允許您在給定範圍內更改說明符的含義來支援匯入映射中的此用例:
{
"imports" : {
"querystringify" : " /node_modules/querystringify/index.js "
},
"scopes" : {
"/node_modules/socksjs-client/" : {
"querystringify" : " /node_modules/socksjs-client/querystringify/index.js "
}
}
}
(此範例是@zkat 提供的每個應用程式多個版本的幾個野外範例之一。謝謝@zkat!)
透過此映射,在 URL 以/node_modules/socksjs-client/
開頭的任何模組內, "querystringify"
說明符將引用/node_modules/socksjs-client/querystringify/index.js
。否則,頂級映射將確保"querystringify"
引用/node_modules/querystringify/index.js
。
請注意,位於範圍內不會改變位址的解析方式;仍然使用導入映射的基本 URL,而不是範圍 URL 前綴。
作用域以一種故意簡單的方式相互“繼承”,在它們運行時合併但壓倒一切。例如,以下導入映射:
{
"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 "
}
}
}
將作出以下決議:
說明符 | 推薦人 | 結果網址 |
---|---|---|
一個 | /scope1/foo.mjs | /a-1.mjs |
乙 | /scope1/foo.mjs | /b-1.mjs |
c | /scope1/foo.mjs | /c-1.mjs |
一個 | /scope2/foo.mjs | /a-2.mjs |
乙 | /scope2/foo.mjs | /b-1.mjs |
c | /scope2/foo.mjs | /c-1.mjs |
一個 | /scope2/scope3/foo.mjs | /a-2.mjs |
乙 | /scope2/scope3/foo.mjs | /b-3.mjs |
c | /scope2/scope3/foo.mjs | /c-1.mjs |
您可以使用<script>
元素(內聯或帶有src=""
屬性)為應用程式安裝導入映射:
< script type =" importmap " >
{
"imports" : { ... } ,
"scopes" : { ... }
}
</ script >
< script type =" importmap " src =" import-map.importmap " > </ script >
使用src=""
屬性時,產生的 HTTP 回應必須有 MIME 類型application/importmap+json
。 (為什麼不重複使用application/json
?這樣做可能會繞過 CSP。)與模組腳本一樣,請求是在啟用 CORS 的情況下發出的,並且回應始終被解釋為 UTF-8。
由於它們會影響所有導入,因此在完成任何模組解析之前,所有導入映射都必須存在並成功獲取。這意味著在導入映射獲取時模組圖獲取被阻止。
這意味著強烈建議使用內聯形式的導入映射以獲得最佳效能。這類似於內嵌關鍵 CSS 的最佳實踐;這兩種類型的資源都會阻止您的應用程式執行重要工作,直到它們被處理為止,因此引入第二個網路往返(甚至磁碟快取往返)是一個壞主意。如果您決定使用外部匯入映射,您可以嘗試使用 HTTP/2 Push 或捆綁 HTTP 交換等技術來減輕此往返損失。
作為導入映射如何影響所有導入的另一個結果,在任何模組圖獲取開始後嘗試添加新的<script type="importmap">
是一個錯誤。導入映射將被忽略,並且<script>
元素將觸發error
事件。
目前,頁面上僅允許有一個<script type="importmap">
。一旦我們弄清楚組合多個導入映射的正確語義,我們計劃在將來擴展它。請參閱 #14、#137 和 #167 中的討論。
我們在工人中做什麼?可能是new Worker(someURL, { type: "module", importMap: ... })
?或者你應該從工人內部設置它?專職工作人員是否應該預設使用還是始終使用其控製文件的地圖?在#2 中討論。
上述規則意味著您可以動態產生導入映射,只要在執行任何導入之前執行此操作即可。例如:
< 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 >
更實際的範例可能會使用此功能來根據特徵檢測來組裝導入映射:
< 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 >
請注意(與其他<script>
元素一樣)在將<script type="importmap">
插入到文件中後修改它的內容將不起作用。這就是為什麼我們在創建和插入<script type="importmap">
之前透過組裝導入映射的內容來編寫上面的範例。
導入映射是應用程式級別的東西,有點像服務工作者。 (更正式地說,它們將是每個模組的映射,因此也是每個領域的映射。)它們並不是要組合的,而是由具有 Web 應用程式整體視圖的人員或工具產生。例如,庫包含導入映射是沒有意義的;庫可以簡單地透過說明符引用模組,並讓應用程式決定這些說明符映射到哪些 URL。
除了一般的簡單性之外,這也是對<script type="importmap">
進行上述限制的部分原因。
由於應用程式的導入映射會更改模組映射中每個模組的解析演算法,因此它們不會受到模組的來源文字最初是否來自跨來源 URL 的影響。如果您從使用裸匯入說明符的 CDN 載入模組,則需要事先了解該模組為您的應用程式新增了哪些裸匯入說明符,並將它們包含在應用程式的匯入對映中。 (也就是說,您需要知道應用程式的所有傳遞依賴項是什麼。)應用程式作者對每個套件使用哪些 URL 的控制非常重要,這樣他們就可以全面管理模組的版本控制和共用。
大多數瀏覽器都有一個推測性 HTML 解析器,它會在 HTML 解析器等待取得和執行阻塞腳本時嘗試發現 HTML 標記中宣告的資源。儘管在 Whatwg/html#5959 中正在努力這樣做,但尚未指定這一點。本節討論一些需要注意的潛在交互作用。
首先,請注意,儘管據我們所知,目前沒有瀏覽器這樣做,但在以下範例中,推測解析器有可能在等待阻塞腳本https://example.com/blocking-1.js
時取得https://example.com/foo.mjs
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 >
同樣,在以下範例中,瀏覽器可以透過將導入映射作為推測性解析過程的一部分進行解析,推測性地獲取https://example.com/foo.mjs
和https://example.com/bar.mjs
:
<!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 >
這裡需要注意的一個交互是,推測性地解析內聯 JS 模組但不支援導入映射的瀏覽器可能會錯誤地推測此示例:它們可能推測性地獲取https://other.example/bar.mjs
,而不是它映射到https://example.com/bar.mjs
。
更一般地說,基於進口地圖的推測可能會遭受與其他推測相同的錯誤。例如,如果blocking-1.js
的內容是
const el = document . createElement ( "base" ) ;
el . href = "/subdirectory/" ;
document . currentScript . after ( el ) ;
那麼在無導入映射範例中對https://example.com/foo.mjs
的推測性獲取將被浪費,因為到執行模組的實際評估時,我們將重新計算相對說明符"./foo.mjs"
並意識到實際請求的是https://example.com/subdirectory/foo.mjs
。
類似地,對於導入映射情況,如果blocking-2.js
的內容是
document . write ( `<script type="importmap">
{
"imports": {
"foo": "./other-foo.mjs",
"https://other.example/bar.mjs": "./other-bar.mjs"
}
}
</script>` ) ;
那麼https://example.com/foo.mjs
和https://example.com/bar.mjs
的推測性獲取將被浪費,因為新編寫的導入映射將生效,而不是看到的那個內聯在HTML 中。
<base>
元素當文件中存在<base>
元素時,導入映射中的所有 URL 和類似 URL 的說明符都會使用<base>
中的href
轉換為絕對 URL。
< 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 >
如果瀏覽器支援 HTMLScriptElement 的supports(type) 方法, HTMLScriptElement.supports('importmap')
必須傳回 true。
if ( HTMLScriptElement . supports && HTMLScriptElement . supports ( 'importmap' ) ) {
console . log ( 'Your browser supports import maps.' ) ;
}
與 Node.js 不同,在瀏覽器中,我們沒有足夠快的檔案系統來爬行查找模組。因此,我們無法直接實作Node模組解析演算法;它需要為每個import
語句執行多次伺服器往返,當我們不斷收到 404 錯誤時會浪費頻寬和時間。我們需要確保每個import
語句只引起一個 HTTP 請求;這需要某種預先計算措施。
有些人建議使用 JavaScript 掛鉤來自訂瀏覽器的模組解析演算法來解釋每個模組說明符。
不幸的是,這對性能來說是致命的;在模組圖的每個邊緣跳入和退出 JavaScript 會大大減慢應用程式的啟動速度。 (典型的 Web 應用程式有數千個模組,其中有 3-4 倍的導入語句。)您可以想像各種緩解措施,例如將呼叫限制為僅裸導入說明符或要求鉤子採用批次說明符和返回批量的URL,但最終沒有什麼比預計算更好的了。
另一個問題是,即使 Web 開發人員獲得了這個鉤子,也很難想像他們可以編寫有用的映射演算法。 Node.js 有一個,但它是基於重複抓取檔案系統並檢查檔案是否存在;如同上面所討論的,這在網路上是不可行的。通用演算法可行的唯一情況是(a)您從不需要每個子圖定制,即您的應用程式中每個模組只存在一個版本; (b) 工具設法以某種統一的、可預測的方式事先安排模組,以便演算法變成「return /js/${specifier}.js
」。但無論如何,如果我們在這個世界上,聲明式解決方案會更簡單。
目前使用的解決方案(例如,透過 babel-plugin-unpkg 在 unpkg CDN 中)是使用建置工具提前將所有裸導入說明符重寫為其適當的絕對 URL。這也可以在安裝時完成,這樣當您使用 npm 安裝軟體包時,它會自動重寫軟體包的內容以使用絕對或相對 URL,而不是裸露的導入說明符。
這種方法的問題是它不適用於動態import()
,因為不可能靜態分析傳遞給該函數的字串。您可以注入一個修復程序,例如將import(x)
的每個實例變更為import(specifierToURL(x, import.meta.url))
,其中specifierToURL
是建置工具產生的另一個函數。但最終,這是一個相當有漏洞的抽象,而且specifierToURL
函數很大程度上重複了該提案的工作。
乍一看,Service Worker 似乎是進行此類資源轉換的合適地點。我們過去曾討論過尋找某種方法來傳遞說明符以及服務工作者的獲取事件,從而允許它傳回適當的Response
。
但是, Service Worker 在首次載入時不可用。因此,它們不能真正成為載入模組的關鍵基礎設施的一部分。它們只能用作提取之上的漸進增強,否則通常會起作用。
如果您有一個簡單的應用程序,不需要作用域依賴解析,並且有一個包安裝工具,可以輕鬆地重寫包內磁碟上的路徑(與當前版本的npm 不同),那麼您可以使用更簡單的映射。例如,如果您的安裝工具建立了以下形式的平面列表
node_modules_flattened/
lodash/
index.js
core.js
fp.js
moment/
index.js
html-to-dom/
index.js
那麼您需要的唯一資訊是
/node_modules_flattened/
)index.js
)您可以想像一個模組導入配置格式僅指定這些內容,甚至僅指定某些子集(如果我們對其他內容進行假設)。
這個想法不適用於需要範圍解析的更複雜的應用程序,因此我們認為完整的導入地圖提案是必要的。但它對於簡單的應用程式仍然有吸引力,我們想知道是否有一種方法可以使提案也有一個簡單模式,不需要列出所有模組,而是依賴約定和工具來確保需要最少的映射。在#7 中討論。
人們多次提出希望為每個模組提供元資料;例如,完整性元資料或取得選項。儘管有些人建議使用 import 語句來執行此操作,但仔細考慮這些選項會導致更喜歡帶外清單檔案。
導入映射可以是該清單檔案。然而,由於以下幾個原因,它可能不是最合適的:
按照目前的設想,應用程式中的大多數模組在導入映射中不會有條目。主要用例是需要透過裸說明符引用的模組,或需要執行一些棘手操作(例如填充或虛擬化)的模組。如果我們設想每個模組都在地圖中,我們就不會包含像packages-via-trailing-slashes這樣的便利功能。
到目前為止,所有建議的元資料都適用於任何類型的資源,而不僅僅是 JavaScript 模組。解決方案可能應該在更通用的層面上發揮作用。
多個<script type="importmap">
出現在頁面上是很自然的,就像多個其他類型的<script>
一樣。我們希望將來能夠實現這一點。
這裡最大的挑戰是決定多個導入映射如何組成。也就是說,給定兩個導入映射都重新映射相同的 URL,或者兩個覆蓋相同 URL 前綴空間的範圍定義,對頁面的影響應該是什麼?目前領先的候選者是級聯解析,它將導入映射從導入說明符→ URL 映射重新轉換為一系列級聯的導入說明符→ 導入說明符映射,最終在「可獲取的導入說明符」(本質上是一個URL)中觸底。
請參閱這些未解決的問題以進行更多討論。
某些用例需要一種從腳本讀取或操作領域的導入映射的方法,而不是透過插入宣告式<script type="importmap">
元素。將其視為一種“導入地圖物件模型”,類似於允許操作頁面的通常聲明性 CSS 規則的 CSS 物件模型。
這裡的挑戰在於如何協調聲明性導入映射與任何程式設計更改,以及在頁面生命週期中何時可以運行此類 API。一般來說,越簡單的設計功能越弱,並且可能滿足的用例也越少。
請參閱這些未解決的問題,以取得更多討論和程式設計 API 可以提供協助的用例。
import.meta.resolve()
建議的import.meta.resolve(specifier)
函數允許模組腳本隨時將導入說明符解析為 URL。有關更多信息,請參閱whatwg/html#5572。這與導入映射相關,因為它允許您解析“包相關”資源,例如
const url = import . meta . resolve ( "somepackage/resource.json" ) ;
將為您提供由頁面導入映射控制的somepackage/
命名空間內的resource.json
的適當映射位置。
社區的一些成員一直致力於與導入地圖相關的填充和工具的開發。以下是我們所知道的:
package.json
和node_modules/
目錄產生導入映射。package.json
產生導入映射。<script type="systemjs-importmap">
的匯入映射。請隨時發送包含更多內容的拉取請求!此外,您可以在問題追蹤器中使用#146 來討論此空間。
該文件源自於 @domenic、@hiroshige-g、@justinfagnani、@MylesBorins 和 @nyaxt 為期一天的衝刺。從那時起,@guybedford 在原型設計和推動對該提案的討論方面發揮了重要作用。
也要感謝問題追蹤器上的所有貢獻者在改進提案方面提供的幫助!