Service Worker 中的 ES 模組

importScripts() 的現代替代方案。

背景

ES 模組一直以來都是開發人員的最愛。除了其他多項優點外,這類模組還提供通用模組格式,可讓共用程式碼發布一次,並在瀏覽器和 Node.js 等替代執行階段中執行。雖然所有新式瀏覽器都提供部分 ES 模組支援,但並非所有瀏覽器都支援所有可執行程式碼的地方。具體來說,在瀏覽器的 Service Worker 中匯入 ES 模組的支援功能,才剛開始廣泛推出。

本文將詳細說明常見瀏覽器中 Service Worker 的 ES 模組支援現況,以及應避免的注意事項,以及提供回溯相容 Service Worker 程式碼的最佳做法。

用途

在 Service Worker 中使用 ES 模組的理想用途,是載入新式程式庫或設定程式碼,並與支援 ES 模組的其他執行階段共用。

在 ES 模組推出前,若要以這種方式共用程式碼,必須使用舊版「通用」模組格式 (例如 UMD),其中包含不必要的樣板,並編寫程式碼來變更全域公開的變數。

如果透過 ES 模組匯入的指令碼內容有所變更,則會觸發 Service Worker 更新流程,這與 importScripts() 的行為一致。

目前限制

僅限靜態匯入

匯入 ES 模組的方式有兩種:使用 import ... from '...' 語法靜態匯入,或使用 import() 方法動態匯入。在 Service Worker 中,目前僅支援靜態語法。

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

這項限制最終可能會解除,並允許動態 ES 模組匯入。目前,請務必只在 Service Worker 內使用靜態語法。

其他工作者呢?

「專屬」worker (以 new Worker('...', {type: 'module'}) 建構) 中的 ES 模組支援更廣泛,自 80 版起,Chrome 和 Edge 以及近期版本的 Safari 都支援。專屬 worker 支援靜態和動態 ES 模組匯入。

Chrome 和 Edge 自第 83 版起,支援共用 worker 中的 ES 模組,但目前沒有其他瀏覽器提供支援。

不支援匯入對應

匯入對應可讓執行階段環境重寫模組指定碼,例如在偏好的 CDN 網址前加上前置字元,以便載入 ES 模組。

雖然 Chrome 和 Edge 89 以上版本支援匯入對應,但目前無法搭配 Service Worker 使用。

瀏覽器支援

Chrome 和 Edge 自第 91 版起,支援 Service Worker 中的 ES 模組。

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

範例程式碼

以下是在網頁應用程式的 window 內容中使用共用 ES 模組,同時註冊使用相同 ES 模組的 Service Worker 的基本範例:

// 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);
    // ...
  })());
});

回溯相容性

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

如要支援不具備內建支援功能的瀏覽器,您可以透過與 ES 模組相容的套件組合器執行 Service Worker 指令碼,建立包含所有模組程式碼內嵌的 Service Worker,並在舊版瀏覽器中運作。或者,如果您嘗試匯入的模組已採用 IIFE 或 UMD 格式,則可以使用 importScripts() 匯入。

當您有兩個版本的 Service Worker 可用 (一個使用 ES 模組,另一個不使用),您需要偵測目前瀏覽器支援的功能,並註冊對應的 Service Worker 指令碼。目前偵測支援的最佳做法仍在變動中,但您可以追蹤這個 GitHub 問題中的討論,取得建議。