我們在前面章節中介紹的匯出和匯入語法稱為「靜態」。語法非常簡單且嚴格。
首先,我們無法動態產生 import
的任何參數。
模組路徑必須是原始字串,不能是函式呼叫。以下寫法無法運作
import ... from getModuleName(); // Error, only from "string" is allowed
其次,我們無法有條件或在執行階段匯入
if(...) {
import ...; // Error, not allowed!
}
{
import ...; // Error, we can't put import in any block
}
這是因為 import
/export
的目的是提供程式碼結構的骨幹。這是一件好事,因為程式碼結構可以被分析,模組可以被收集並由特殊工具打包成一個檔案,未使用的匯出可以被移除(「樹狀搖晃」)。這只有在匯入/匯出的結構簡單且固定的情況下才有可能。
但是,我們如何才能動態匯入一個模組,依需求而定?
import() 表達式
import(module)
表達式會載入模組,並傳回一個 Promise,這個 Promise 會解析成一個包含所有匯出的模組物件。它可以在程式碼中的任何地方呼叫。
我們可以在程式碼的任何地方動態使用它,例如
let modulePath = prompt("Which module to load?");
import(modulePath)
.then(obj => <module object>)
.catch(err => <loading error, e.g. if no such module>)
或者,如果在非同步函式中,我們可以使用 let module = await import(modulePath)
。
例如,如果我們有以下模組 say.js
// 📁 say.js
export function hi() {
alert(`Hello`);
}
export function bye() {
alert(`Bye`);
}
…那麼動態匯入可以像這樣
let {hi, bye} = await import('./say.js');
hi();
bye();
或者,如果 say.js
有預設匯出
// 📁 say.js
export default function() {
alert("Module loaded (export default)!");
}
…那麼,為了存取它,我們可以使用模組物件的 default
屬性
let obj = await import('./say.js');
let say = obj.default;
// or, in one line: let {default: say} = await import('./say.js');
say();
以下是完整範例
結果
say.js
index.html
export function hi() {
alert(`Hello`);
}
export function bye() {
alert(`Bye`);
}
export default function() {
alert("Module loaded (export default)!");
}
<!doctype html>
<script>
async function load() {
let say = await import('./say.js');
say.hi(); // Hello!
say.bye(); // Bye!
say.default(); // Module loaded (export default)!
}
</script>
<button onclick="load()">Click me</button>
請注意
動態匯入在一般指令碼中運作,它們不需要 script type="module"
。
請注意
儘管 import()
看起來像函式呼叫,但它是一種特殊語法,碰巧使用括號(類似於 super()
)。
所以我們無法將 import
複製到變數或對它使用 call/apply
。它不是函式。
留言
<code>
標籤,對於多行 - 將它們包在<pre>
標籤中,對於超過 10 行 - 使用沙盒 (plnkr、jsbin、codepen…)