動態 import()

發佈於 · 標籤為 ECMAScript ES2020

動態 import() 引入了新的類似函式的 import 形式,與靜態 import 相比,解鎖了新的功能。本文比較兩者,並概述新功能。

靜態 import(回顧) #

Chrome 61 支援 模組 中的 ES2015 import 陳述式。

考慮位於 ./utils.mjs 的模組

// Default export
export default () => {
console.log('Hi from the default export!');
};

// Named export `doStuff`
export const doStuff = () => {
console.log('Doing stuff…');
};

以下是靜態匯入和使用 ./utils.mjs 模組的方法

<script type="module">
import * as module from './utils.mjs';
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
</script>

注意: 前一個範例使用 .mjs 副檔名來表示它是模組,而不是一般指令碼。在網路上,檔案副檔名並不太重要,只要檔案以正確的 MIME 類型(例如 JavaScript 檔案的 text/javascript)在 Content-Type HTTP 標頭中提供服務即可。

.mjs 副檔名在其他平台上特別有用,例如 Node.jsd8,這些平台沒有 MIME 類型或其他強制掛鉤(例如 type="module")的概念來判斷某個項目是模組還是一般指令碼。我們在此使用相同的副檔名,以確保跨平台的一致性,並清楚區分模組和一般指令碼。

這種匯入模組的語法形式是靜態宣告:它只接受字串文字作為模組指定符,並透過執行前的「連結」程序將繫結引入區域範圍。靜態 import 語法只能用於檔案的最上層。

靜態 import 能夠支援重要的使用案例,例如靜態分析、套件工具和樹狀搖晃。

在某些情況下,很有用

  • 依需求(或有條件地)匯入模組
  • 在執行階段計算模組指定符
  • 從一般指令碼(與模組相反)匯入模組

這些都不可能使用靜態 import

動態 import() 🔥 #

動態 import() 導入一種新的 import 函式形式,用於滿足這些使用案例。import(moduleSpecifier) 會傳回一個承諾,用於取得所要求模組的模組命名空間物件,此物件會在擷取、實例化和評估模組的所有相依項以及模組本身後建立。

以下是動態匯入和使用 ./utils.mjs 模組的方法

<script type="module">
const moduleSpecifier = './utils.mjs';
import(moduleSpecifier)
.then((module) => {
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
});
</script>

由於 import() 會傳回一個承諾,因此可以使用 async/await,而不是基於 then 的呼叫回樣式

<script type="module">
(async () => {
const moduleSpecifier = './utils.mjs';
const module = await import(moduleSpecifier)
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
})();
</script>

注意:儘管 import() 看起來 像函式呼叫,但它被指定為語法,只會使用括號(類似於 super())。這表示 import 沒有繼承自 Function.prototype,因此您無法 callapply 它,而且像 const importAlias = import 之類的東西無法運作,說真的,import 甚至不是一個物件!不過,在實務上這並不要緊。

以下是一個範例,說明動態 import() 如何在小型單頁應用程式中於導覽時延遲載入模組

<!DOCTYPE html>
<meta charset="utf-8">
<title>My library</title>
<nav>
<a href="books.html" data-entry-module="books">Books</a>
<a href="movies.html" data-entry-module="movies">Movies</a>
<a href="video-games.html" data-entry-module="video-games">Video Games</a>
</nav>
<main>This is a placeholder for the content that will be loaded on-demand.</main>
<script>
const main = document.querySelector('main');
const links = document.querySelectorAll('nav > a');
for (const link of links) {
link.addEventListener('click', async (event) => {
event.preventDefault();
try {
const module = await import(`/${link.dataset.entryModule}.mjs`);
// The module exports a function named `loadPageInto`.
module.loadPageInto(main);
} catch (error) {
main.textContent = error.message;
}
});
}
</script>

正確套用時,動態 import() 所啟用的延遲載入功能非常強大。為了示範,Addy 修改了 一個範例 Hacker News PWA,該 PWA 在第一次載入時靜態匯入了所有相依項,包括留言。 更新版本 使用動態 import() 延遲載入留言,避免在使用者真正需要留言之前載入、剖析和編譯留言。

注意:如果您的應用程式從另一個網域匯入指令碼(無論是靜態或動態),則需要傳回具有有效 CORS 標頭(例如 Access-Control-Allow-Origin: *)的指令碼。這是因為與一般指令碼不同,模組指令碼(及其匯入)會透過 CORS 擷取。

建議 #

靜態 import 和動態 import() 都很有用。它們各自有非常不同的使用案例。對於初始繪製相依項,特別是對於折疊上方內容,請使用靜態 import。在其他情況下,請考慮使用動態 import() 依需求載入相依項。

動態 import() 支援 #