2020 年 2 月 8 日

動態匯入

我們在前面章節中介紹的匯出和匯入語法稱為「靜態」。語法非常簡單且嚴格。

首先,我們無法動態產生 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。它不是函式。

教學地圖

留言

留言前請先閱讀…
  • 如果你有建議要改進 - 請 提交 GitHub 議題 或發起拉取請求,而不是留言。
  • 如果你無法理解文章中的某些內容 - 請詳細說明。
  • 若要插入幾行程式碼,請使用 <code> 標籤,對於多行 - 將它們包在 <pre> 標籤中,對於超過 10 行 - 使用沙盒 (plnkrjsbincodepen…)