Service Worker 中的 ES 模組

importScripts() 的新型替代方案。

背景

一直以來,ES 模組深受開發人員喜愛。除了其他許多好處之外,他們也提供通用的模組格式,共用程式碼只要發布一次,然後在瀏覽器和 Node.js 等替代執行階段中執行即可。雖然所有新式瀏覽器都支援部分 ES 模組,但並非全部都支援「所有」可執行程式碼的位置。具體來說,在瀏覽器的服務工作站中匯入 ES 模組的支援功能即將擴大支援範圍。

本文詳細介紹在通用瀏覽器中,服務工作站目前支援的 ES 模組狀態,以及要避免的一些問題,以及運送回溯相容的 Service Worker 程式碼的最佳做法。

用途

服務工作站內部 ES 模組的理想用途,是載入與支援 ES 模組的其他執行階段共用的新型程式庫或設定程式碼。

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

透過 ES 模組匯入的指令碼,如果內容有所變更,且符合 importScripts()行為,就可能會觸發 Service Worker 更新流程。

目前限制

僅限靜態匯入

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

這項限制類似於針對 importScripts() 用量設下的類似限制。對 importScripts() 的動態呼叫不會在 Service Worker 內運作,且所有會同步的 importScripts() 呼叫都必須在 Service Worker 完成其 install 階段前完成。這項限制可確保瀏覽器知道且可隱含快取服務工作處理程序在安裝期間實作所需的所有 JavaScript 程式碼。

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

其他員工呢?

「專用」工作站 (即使用 new Worker('...', {type: 'module'}) 建構的 ES 模組) 的支援範圍較廣,且自 80 版以來,以及 Safari 的最新版本,已受到 Chrome 和 Edge 支援。專屬工作站支援靜態和動態 ES 模組匯入作業。

83 版起,Chrome 和 Edge 都支援共用工作站中的 ES 模組,但目前沒有其他瀏覽器支援。

不支援匯入地圖

匯入對應可讓執行階段環境重新編寫模組指定碼,例如加上可載入 ES 模組的偏好 CDN 網址之後。

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

瀏覽器支援

91 版起,Chrome 和 Edge 即支援 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);
    // ...
  })());
});

回溯相容性

如果所有瀏覽器都支援服務工作站中的 ES 模組,上述範例就適用,但截至本文撰寫時,此範例並不正確。

為了配合未內建支援功能的瀏覽器,您可以透過 ES 模組相容的 Bundler 執行服務工作站指令碼,建立包含所有內嵌模組程式碼的服務工作站,而且可在舊版瀏覽器中運作。或者,如果您嘗試匯入的模組已隨附於 IIFEUMD 格式,您可以使用 importScripts() 匯入這些模組。

當您有兩個可用的 Service Worker 版本 (一個使用 ES 模組,而另一個版本則沒有) 後,就需要偵測目前瀏覽器支援的內容,並註冊對應的 Service Worker 指令碼。偵測支援的最佳做法目前已經不敷使用,但您可以按照這個 GitHub 問題中的討論,取得相關建議。

_相片來源:Vlado PaunovicUnsplash 網站上