本篇介紹 ES2020 (ES11) 提供的 import()
。
在過去,模組只能用靜態宣告的方式 import,接收的字串字面值 (literal) 是 module specifier,並透過 pre-runtime 的 linking 過程將 binding 至 local scope。所以可以用於 static analysis、bundling tools 和 tree shaking。
但無法在 runtime 時動態 import 模組,常見的情境如下:
所以這就是此提案 import()
被提出的原因。
import()
import()
是一種 function-like 的模組載入語法形式,spec 的定義如下
import(specifier)
會為請求 (requested) 模組的模組 namespace 物件回傳一個 Promise
,該 Promise
是在 fetching、instantiating 和 evaluating 模組的所有依賴和模組本身之後建立的。
其中的 specifier
和 import
宣告式中的解譯方式 (interpreted) 相同,即相同的字串可以在 import()
和 import
宣告式使用:
import './modules/module-name';
import('./modules/module-name');
但 import()
可以用模板字串,而 import
宣告式不能這樣 import:
let moduleName = 'module-name';
import(`./modules/${moduleName}.js`);
在 script 或 module 使用 import()
可以非同步的 import 模組。因為 import()
會回傳 Promise
,所以常見有兩種寫法:
Promise.prototype.then()
async
/ await
import('/modules/module-name.js')
.then(module => {
console.log(module);
});
let module = await import('./modules/module-name.js');
下面使用 import()
實作一個簡易的 SPA,並且能延遲載入模組,從模組內引入的內容渲染在畫面上:
<!-- index.html -->
<nav>
<a href="/books" data-entry-module="books">Books</a>
<a href="/movies" data-entry-module="movies">Movies</a>
<a href="/not-found" data-entry-module="not-found">404</a>
</nav>
<main>Content will load here!</main>
<script src="main.js"></script>
下面是 main.js
,當點擊連結時,不會跳頁,而是動態 import 對應的模組,並執行模組中的 loadPageInto()
,更新 main
元素的內容:
// ./main.js
const main = document.querySelector('main');
const links = [...document.querySelectorAll("nav > a")];
for (const link of links) {
link.addEventListener("click", event => lazyLoadingModules(event, link));
}
async function lazyLoadingModules(event, link) {
event.preventDefault();
try {
const moudlePath = `./modules/${link.dataset.entryModule}.js`;
const module = await import(moudlePath);
module.loadPageInto(main);
} catch (error) {
main.textContent = error.message;
}
}
下面是 books
和 movies
模組中各別的 loadPageInto()
:
// ./modules/books.js
export function loadPageInto(main) {
main.innerHTML = "Books";
}
// ./modules/movies.js
export function loadPageInto(main) {
main.innerHTML = "Movies";
}
如下圖,我故意沒有準備最後一個連結的 script,因此會因 import 失敗而列印出錯誤訊息:
範例程式碼連結:簡易 SPA - StackBlitz
import()
| 2ality
import()
- dynamically importing ES modules | 2ality