動態 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.js 和 d8
,這些平台沒有 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
,因此您無法 call
或 apply
它,而且像 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()
支援 #
- Chrome: 自版本 63 起支援
- Firefox: 自版本 67 起支援
- Safari: 自版本 11.1 起支援
- Node.js: 自版本 13.2 起支援
- Babel: 支援