iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 23
2
Modern Web

JavaScript 之旅系列 第 23

JavaScript 之旅 (23):Dynamic import()

本篇介紹 ES2020 (ES11) 提供的 import()

前言

在過去,模組只能用靜態宣告的方式 import,接收的字串字面值 (literal) 是 module specifier,並透過 pre-runtime 的 linking 過程將 binding 至 local scope。所以可以用於 static analysis、bundling tools 和 tree shaking。

但無法在 runtime 時動態 import 模組,常見的情境如下:

  • 在 runtime 時,才能依當前情況 import 對應的模組,例如:使用者的語系
  • 為了減少第一次的載入時間,所以只有在需要時才動態 import 模組
  • 兩個模組互相競爭 (racing),只選擇第一個成功載入的模組
  • ...等

所以這就是此提案 import() 被提出的原因。

import()

import() 是一種 function-like 的模組載入語法形式,spec 的定義如下

import(specifier) 會為請求 (requested) 模組的模組 namespace 物件回傳一個 Promise,該 Promise 是在 fetching、instantiating 和 evaluating 模組的所有依賴和模組本身之後建立的。

其中的 specifierimport 宣告式中的解譯方式 (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');

範例:簡易 SPA

下面使用 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;
  }
}

下面是 booksmovies 模組中各別的 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

資料來源


上一篇
JavaScript 之旅 (22):BigInt
下一篇
JavaScript 之旅 (24):Optional Chaining Operator ( ?. 運算子)
系列文
JavaScript 之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言