Service Worker 中的 ES 模組

importScripts() 的新型替代方案。

背景

一直以來,ES 模組深受開發人員喜愛。除了其他多項優點之外,它們還提供通用模組格式,讓共用程式碼可一次發布,並在瀏覽器和 Node.js 等其他執行階段中執行。雖然所有新型瀏覽器都提供某些 ES 模組支援功能,但並非所有瀏覽器都支援所有可執行程式碼的環境。具體來說,在瀏覽器的服務工作者中匯入 ES 模組的支援功能才剛開始廣泛提供。

本文將詳細說明在常見瀏覽器中,服務工作者支援 ES 模組的現況,以及一些應避免的陷阱,以及提供向後相容服務工作者程式碼的最佳做法。

用途

服務工作者中 ES 模組的理想用途,是載入與支援 ES 模組的其他執行階段共用的現代程式庫或設定程式碼。

如果要在 ES 模組之前以這種方式分享程式碼,就必須使用包含非必要的樣板的舊版「通用」模組格式 (例如 UMD),以及編寫對全域公開變數進行變更的程式碼。

如果透過 ES 模組匯入的劇本內容發生變更,可以觸發服務工作者更新流程,以符合 importScripts()行為

目前限制

僅限靜態匯入

您可以透過下列兩種方式匯入 ES 模組:使用 import ... from '...' 語法的靜態方式,或使用 import() 方法的動態方式。在 Service Worker 中,目前只支援靜態語法。

這項限制類似於 importScripts() 用量的類似限制。對 importScripts() 的動態呼叫無法在服務工作站內運作,且所有 importScripts() 呼叫 (本質上為同步) 必須在服務工作站完成 install 階段前完成。這項限制可確保瀏覽器知道服務工作者在安裝期間執行時所需的所有 JavaScript 程式碼,並能將這些程式碼隱含快取。

這項限制最終可能會解除,屆時可能會允許動態 ES 模組匯入。目前,請確保您僅使用 Service Worker 中的靜態語法。

其他員工呢?

在「專用」worker 中支援 ES 模組 (使用 new Worker('...', {type: 'module'}) 建構的模組) 更為普遍,自 Chrome 和 Edge 第 80 版起,以及 Safari 近期版本,都已支援這項功能。專屬工作站支援靜態和動態 ES 模組匯入作業。

83 版起,Chrome 和 Edge 就已在共用工作項中支援 ES 模組,但目前其他瀏覽器尚未提供支援。

不支援匯入地圖

匯入對應可讓執行階段環境重新撰寫模組指定詞,例如在 ES 模組可載入的偏好 CDN 前方加上網址。

雖然 Chrome 和 Edge 89 以上版本支援匯入地圖,但目前無法與服務 worker 搭配使用

瀏覽器支援

91 版起,Chrome 和 Edge 支援服務工作程式中的 ES 模組。

Safari 在技術預覽版 122 版本中新增了支援功能,開發人員應該會在日後的 Safari 穩定版中看到這項功能。

範例程式碼

以下是基本範例,說明如何在網路應用程式的 window 情境中使用共用 ES 模組,同時註冊使用相同 ES 模組的服務工作者:

// Inside config.js:
export const cacheName = 'my-cache';
// Inside your web app:
<script type="module">
  import {cacheName} from './config.js';
  // Do something with cacheName.

  await navigator.serviceWorker.register('es-module-sw.js', {
    type: 'module',
  });
</script>
// Inside es-module-sw.js:
import {cacheName} from './config.js';

self.addEventListener('install', (event) => {
  event.waitUntil((async () => {
    const cache = await caches.open(cacheName);
    // ...
  })());
});

回溯相容性

如果所有瀏覽器都支援服務工作者中的 ES 模組,上述範例就能正常運作,但截至本文撰寫時,情況並非如此。

為了支援沒有內建支援的瀏覽器,您可以透過ES 模組相容的 bundler執行服務工作者指令碼,建立內嵌所有模組程式碼的服務工作者,並在舊版瀏覽器中運作。或者,如果您嘗試匯入的模組已以 IIFEUMD 格式提供,您可以使用 importScripts() 匯入這些模組。

當您有兩個服務工作者版本 (一個使用 ES 模組,另一個則不使用) 時,就必須偵測目前瀏覽器支援的版本,並註冊對應的服務工作者指令碼。目前偵測支援功能的最佳做法仍在變動中,但您可以參考這個 GitHub 問題中的討論內容,瞭解相關建議。

_相片來源:Vlado Paunovic 提供,取自 Unsplash_