工作箱視窗

workbox-window 套件是一組模組,用來在 window 項背景資訊,也就是 你的實際內容因為前者是其他工作箱的不足之處 在 Service Worker 中執行的套件。

workbox-window 的主要特色/目標為:

匯入及使用 Workbox 視窗

workbox-window 套件的主要進入點為 Workbox 類別,而 您可以從我們的 CDN 或 JavaScript 統合工具

使用我們的 CDN

要從我們的 CDN 匯入網站的 Workbox 類別,最簡單的方法就是:

<script type="module">
  import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-window.prod.mjs';

  if ('serviceWorker' in navigator) {
    const wb = new Workbox('/sw.js');

    wb.register();
  }
</script>

請注意,本範例使用 <script type="module">import 陳述式: 載入 Workbox 類別。雖然您可能會覺得 您必須把這個 程式碼,以便在舊版瀏覽器中正常運作,但其實並非必要。

所有支援 Service 工作站的主要瀏覽器也會支援原生 JavaScript 模組,因此完全 可以將這個程式碼提供給任何瀏覽器 (舊版瀏覽器只會忽略這段程式碼)。

使用 JavaScript 軟體包載入 Workbox

雖然 workbox-window 絕對不需要工具,但如果您的 開發基礎架構已包括許多整合工具 可使用的 webpackRollupnpm 依附元件搭配使用,即可將其用於 載入 workbox-window

首先 安裝 workbox-window 做為應用程式的依附元件:

npm install workbox-window

接著,在應用程式的其中一個 JavaScript 檔案中,import 工作箱是由 參照 workbox-window 套件名稱:

import {Workbox} from 'workbox-window';

if ('serviceWorker' in navigator) {
  const wb = new Workbox('/sw.js');

  wb.register();
}

如果您的 Bundler 支援透過動態匯入陳述式分割程式碼, 您也可以有條件地載入 workbox-window,這應該有助於減少 網頁主要素材資源組合的大小

雖然 workbox-window 很小,但其實並不合理 需要與網站的核心應用程式邏輯 (即服務工作處理程序) 載入 從大自然角度來看,都是進步的一大進步

if ('serviceWorker' in navigator) {
  const {Workbox} = await import('workbox-window');

  const wb = new Workbox('/sw.js');
  wb.register();
}

進階郵件分類概念

與在 Service Worker 中執行的 Workbox 套件不同,建構檔案 由 workbox-windowmainmodule 欄位位於 package.json 已轉成 ES5。因此與當今的 以及建立工具,其中有些不允許開發人員傳輸 其 node_module 依附元件。

如果您的建構系統「確實」允許您轉譯依附元件 (或 您不必轉譯任何程式碼) 而非套件本身。

以下列出匯入 Workbox 的各種方法,以及 分別傳回的結果:

// Imports a UMD version with ES5 syntax
// (pkg.main: "build/workbox-window.prod.umd.js")
const {Workbox} = require('workbox-window');

// Imports the module version with ES5 syntax
// (pkg.module: "build/workbox-window.prod.es5.mjs")
import {Workbox} from 'workbox-window';

// Imports the module source file with ES2015+ syntax
import {Workbox} from 'workbox-window/Workbox.mjs';
敬上

範例

匯入 Workbox 類別後,即可使用該類別註冊 必須與 Service Worker 互動,以下列舉幾個可能的應用方式 應用程式中的 Workbox

註冊 Service Worker,並在首次啟用 Service Worker 時通知使用者

許多網頁應用程式的使用者 Service Worker 都能預先快取資產,讓應用程式正常運作 離線載入檔案在某些情況下 使用者將應用程式恢復為可離線使用。

const wb = new Workbox('/sw.js');

wb.addEventListener('activated', event => {
  // `event.isUpdate` will be true if another version of the service
  // worker was controlling the page when this version was registered.
  if (!event.isUpdate) {
    console.log('Service worker activated for the first time!');

    // If your service worker is configured to precache assets, those
    // assets should all be available now.
  }
});

// Register the service worker after event listeners have been added.
wb.register();

如果 Service Worker 已安裝,但仍無法順利啟用,請通知使用者

當現有 Service Worker 控制的網頁註冊新的服務時 根據預設,Service Worker 要等到所有用戶端,才會啟動 由初始 Service Worker 控制的所有元件已完全卸載。

這是開發人員常會感到困惑,尤其是在 重新載入目前網頁不會導致新的 Service Worker 啟用

為了盡量減少混淆 並清楚掌握狀況 Workbox 類別提供 waiting 事件,您可以監聽:

const wb = new Workbox('/sw.js');

wb.addEventListener('waiting', event => {
  console.log(
    `A new service worker has installed, but it can't activate` +
      `until all tabs running the current version have fully unloaded.`
  );
});

// Register the service worker after event listeners have been added.
wb.register();

通知使用者 workbox-broadcast-update 套件的快取更新

workbox-broadcast-update 套件可讓您從快取提供內容 (以便快速傳遞),同時 能夠通知使用者更新內容 (使用 「過時」重新驗證策略)。

如要從視窗接收這些更新,您可以監聽message事件的 類型 CACHE_UPDATED

const wb = new Workbox('/sw.js');

wb.addEventListener('message', event => {
  if (event.data.type === 'CACHE_UPDATED') {
    const {updatedURL} = event.data.payload;

    console.log(`A newer version of ${updatedURL} is available!`);
  }
});

// Register the service worker after event listeners have been added.
wb.register();

將要快取的網址清單傳送給 Service Worker

在某些應用程式中,系統可能會 但在建構階段預先快取,但有些應用程式提供完全不同的頁面, 以使用者首先到達的網址為依據

如果應用程式屬於後者,只快取資產就適合 也就是使用者造訪特定網頁所需的時間。使用 workbox-routing 套件,您可以 將網址清單傳送給路由器進行快取,系統便會根據 例如路由器本身所定義的規則

此範例會將網頁載入的網址清單傳送至路由器 會啟動新的 Service Worker。請注意,您可以傳送「所有」網址,因為 系統會快取符合 Service Worker 中已定義路徑的網址。

const wb = new Workbox('/sw.js');

wb.addEventListener('activated', event => {
  // Get the current page URL + all resources the page loaded.
  const urlsToCache = [
    location.href,
    ...performance.getEntriesByType('resource').map(r => r.name),
  ];
  // Send that list of URLs to your router in the service worker.
  wb.messageSW({
    type: 'CACHE_URLS',
    payload: {urlsToCache},
  });
});

// Register the service worker after event listeners have been added.
wb.register();
敬上

重要的 Service Worker 生命週期時刻

服務工作站生命週期 對我們來說,是相當複雜的挑戰,可能並不容易理解。部分原因在於 而是必須處理所有可能使用的極端情況 Service Worker (例如註冊多個 Service Worker、註冊 Service Worker 會註冊服務工作處理程序 不同的名稱等等)。

不過,大多數實作 Service Worker 的開發人員都不需要擔心 這些極端情況,因為其使用很簡單。多數開發人員 每次載入網頁時,系統只會註冊一個 Service Worker,而且不會變更 Service Worker 的名稱 部署到伺服器中

Workbox 類別採用這個較簡單的 Service Worker 生命週期檢視畫面 將所有 Service Worker 註冊分為兩個類別:執行個體的 已註冊的 Service Worker 和外部 Service Worker:

  • 已註冊的 Service Worker:開始安裝為 Workbox 例項呼叫 register() 或已經啟用的項目 Service Worker 表示呼叫 register() 時,不會在登錄時觸發 updatefound 事件。
  • External Service Worker:開始安裝的 Service Worker 單獨呼叫 register()Workbox 例項。這通常 當使用者在其他分頁中開啟您的網站時,就會發生這個事件。如果 事件源自外部 Service Worker,事件的 isExternal 屬性會設為 true

以下說明這兩種 Service Worker 的特性。 重要服務工作處理程序生命週期時刻,以及開發人員建議 處理方式:

第一次安裝 Service Worker 時

建議您在首次安裝 Service Worker 時進行安裝 與您處理日後所有更新的方式不同

workbox-window 中,您可以先區分版本 安裝及後續更新程式,方法是檢查 isUpdate 後續事件。如果是第一次安裝,isUpdatefalse

const wb = new Workbox('/sw.js');

wb.addEventListener('installed', event => {
  if (!event.isUpdate) {
    // First-installed code goes here...
  }
});

wb.register();
重要時刻 活動 建議採取的行動
已安裝新的 Service Worker (首次安裝) installed

如果 Service Worker 是第一次安裝,常會預先快取 讓網站離線運作所需的一切素材資源。建議考慮採用 通知使用者網站現在可以離線運作。

另外,由於 Service Worker 第一次安裝時,不會 此外,您也可以考慮使用 已載入的素材資源 (如果這些 資產已在友善載入時)。傳送 Service Worker 需要進行快取的網址清單範例,顯示如何執行 而負責任的 AI 技術做法 有助於達成這項目標

Service Worker 已開始控制網頁 controlling

安裝新的 Service Worker 並開始控制頁面後, 所有後續的擷取事件都會透過該 Service Worker 進行。如果您的 Service Worker 會新增任何特殊邏輯,以處理特定擷取事件, 這就是您知道邏輯會執行的時間點

請注意,首次安裝 Service Worker 時, 除非服務工作處理程序,否則不會開始控制目前頁面 通話 clients.claim()。預設 等下一個頁面載入 才能開始控制

workbox-window 的角度來看,這表示 controlling 只有在 Service Worker 會呼叫 clients.claim()。這個活動不是 會在註冊前已控管網頁時傳送。

Service Worker 已完成啟用程序 activated

如前所述,服務工作處理程序首次完成時 啟用這個選項時,系統可能 (或無法) 開始控制網頁。

因此,我們不建議透過監聽啟動事件的方式 讓您可以瞭解 Service Worker 何時能控管頁面。不過, 您在執行中事件 (在 Service Worker 中) 執行邏輯時, 以便瞭解邏輯完成的時間 。

找到 Service Worker 的更新版本時

新的 Service Worker 開始安裝,但目前已有版本 控制網頁時,下列所有事件的 isUpdate 屬性將會 應為 true

在這情況下的回應方式通常與第 種情況不同 安裝,藉此控管使用者接收更新的時間和方式。

重要時刻 活動 建議採取的行動
已安裝新的 Service Worker (更新先前的服務) installed

如果這不是第一次安裝 Service Worker (event.isUpdate === true),表示新版的 已找到並安裝 Service Worker (也就是說,其他版本 目前控制網頁的存取要求)。

這通常表示已部署新版網站 和新資產可能剛完成預先快取。

注意:部分開發人員會使用 installed 事件來提供 使用者,他們可以使用新的網站版本。但是,取決於 你有打電話嗎? skipWaiting() (位於安裝 Service Worker 中), 已安裝的 Service Worker 不一定會立即啟用。如果發生以下情況: 呼叫 skipWaiting(),最好將資訊告知使用者 新的 Service Worker 啟用後就會更新 「不要」呼叫 skipWaiting 最好告知他們 在等待事件中處理的待處理更新 (詳情請參閱下方說明)。

已安裝 Service Worker,但一直卡在等候階段 waiting

如果 Service Worker 的更新版本未呼叫 skipWaiting(),因此無法安裝 持續啟用,直到目前運作中的 Service Worker 控制的所有頁面為止 已卸載。建議您通知使用者有可用的更新 且會在他們下次造訪時套用。

警告!開發人員常常提示 重新載入即可更新,但在許多情況下,請 重新整理網頁並不會啟動已安裝的工作站。如果 使用者重新整理網頁後,Service Worker 就「仍在」等待, waiting 事件會再次觸發,而且 event.wasWaitingBeforeRegister 屬性會是 true。注意: 我們計劃在未來的版本中改善這個使用體驗。追蹤問題 #1848

另一種方法是提示使用者並詢問 或繼續等待。如果選擇取得更新 使用 postMessage() 告知 Service Worker 要執行 skipWaiting()。查看進階方案 例如提供「重新載入」網頁給使用者

Service Worker 已開始控制網頁 controlling

當更新的 Service Worker 開始控制網頁時, 目前所控制的 Service Worker 版本與 網頁載入時控制的版本。在某些情況下 可能沒有問題,但這也代表 目前網頁已不在快取中 (也可能不在伺服器上)。 建議您告知使用者頁面中的部分內容 可能無法正常運作

注意:controlling 事件不會觸發 您沒有在 Service Worker 中呼叫 skipWaiting()

Service Worker 已完成啟用程序 activated 更新過的 Service Worker 啟動完畢之後, 您在 Service Worker 的 activate 中執行的邏輯 已完成如有任何需要延遲到該邏輯 接著,您就可以執行這項工具

發現 Service Worker 未預期的版本時

有時使用者會讓網站在背景分頁中長時間開啟一段時間 讓應用程式從可以最快做出回應的位置 回應使用者要求訪客甚至可能在未察覺的情況下,開啟新分頁並前往您的網站 他們在背景分頁中開啟網站在這些情況下 您的網站同時執行了兩個版本 可以為您呈現一些有趣的問題

舉例來說,假設您的網站 A 和分頁 B 分別執行 v1 執行 v2載入分頁 B 後,該分頁將由服務版本控管 傳送 v1 版本的工作站,但伺服器傳回網頁 (如果使用 網路優先的快取策略 ) 會包含所有 v2 資產。

不過,這通常不是分頁 B 的問題,因為當您編寫 v2 時 您知道第 1 版程式碼的運作方式了,不過, ,因為第 1 版程式碼無法預測 可能會有什麼影響

為協助處理這類情況,workbox-window 也會調度生命週期 偵測到「外部」的更新時觸發的事件Service Worker 中 「外部」意指任何非現行 Workbox 版本的版本 執行個體已註冊。

從 Workbox v6 以上版本,這些事件等同於 上述,而且在每個事件中加入 isExternal: true 屬性後, 物件。如果網頁應用程式需要實作特定邏輯來處理 「外部」服務工作處理程序,您可以在事件處理常式中查看該屬性。

避免常見錯誤

Workbox 提供開發人員記錄功能,是 Workbox 最實用的功能之一。且 對 workbox-window 而言更是如此。

我們瞭解,透過 Service Worker 進行開發經常令人困惑,而且在過程中 結果會與您的預期有所落差 可能很難察覺

舉例來說,當您變更 Service Worker 並重新載入頁面時, 您的瀏覽器可能不會有這項變更。最有可能的原因是 您的服務工作處理程序還在等候啟用。

但透過 Workbox 類別註冊 Service Worker 時,您將 開發人員主控台中所有生命週期狀態的異動通知 協助偵錯,找出哪些地方不如預期。

工作箱視窗控制台警告 (等待工作站)

此外,開發人員初次使用 Service Worker 時常犯的錯誤為 註冊 Service Worker 範圍錯誤

為避免這種情況發生,Workbox 類別會在出現以下情況時發出警告: 註冊 Service Worker 的網頁不在該 Service Worker 範圍內。是 如果 Service Worker 已啟用但尚未運作,系統也會發出警告 控制網頁:

非控制 worker 的 Workbox 視窗控制台警告

服務工作處理程序的通訊視窗

大多數進階 Service Worker 需要在 Service Worker 和視窗。Workbox 類別也有助於解決這個問題 提供 messageSW() 方法,該方法會postMessage()執行個體的 並等候回應。

儘管您可以傳送任何格式的資料給 Service Worker,但共用的格式 所有 Workbox 套件都屬於一個物件 (包含三個屬性 選用):

屬性 必填與否 類型 說明
type string

專屬字串,用於識別這個訊息。

按照慣例,類型全為大寫,並以底線分隔 還能分析語法及擷取語言資訊 例如字詞之間的關係如果類型代表要採取的動作,則應為指令 以目前時態 (例如 CACHE_URLS) 表示,表示類型代表 遭到檢舉的資訊,必須是過去的時間 (例如 URLS_CACHED)。

meta string 在 Workbox 中,這一律是傳送 撰寫新的電子郵件訊息親自傳送郵件時,可以省略這個屬性,或是 你隨心所欲調整設定
payload * 要傳送的資料。這通常是物件,但不一定要是物件。

透過 messageSW() 方法傳送的訊息會使用 MessageChannel,以便接收端 回應使用者如要回覆訊息,你可以撥打電話 event.ports[0].postMessage(response)messageSW() 方法傳回的承諾會解析為任何 response 您進行回覆時

以下範例說明如何從視窗將訊息傳送至 Service Worker,並 取得回覆第一個程式碼區塊是 而第二個區塊則使用 Workbox 類別將 然後等候回覆:

sw.js 中的程式碼:

const SW_VERSION = '1.0.0';

addEventListener('message', event => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

在 main.js 中的程式碼 (在視窗中執行):

const wb = new Workbox('/sw.js');
wb.register();

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);

管理版本不相容的問題

以上範例說明如何檢查 Service Worker 的實作方式 從視窗下載版本這個範例之所以使用,是因為 因此,關鍵在於 請注意,您的 Service Worker 可能執行的版本不同 網頁程式碼運作期間,要處理此問題 問題取決於是否優先放送網頁 也就是快取優先

網路優先

優先提供網頁聯播網時,您的使用者始終可以 下載最新版本的 HTML。但是使用者初次造訪 Gmail 時 使用者重新造訪您的網站 (部署更新後) 將會取得的 HTML 程式碼 但在瀏覽器中運作的 Service Worker 先前安裝的版本 (可能有許多舊版本)。

請務必瞭解這種可能性,因為 JavaScript 是否已載入 您目前的網頁版本會將訊息傳送到 因此該版本可能不知道該如何回應 ( 使用不相容的格式)。

因此,建議您一律啟用 Service Worker 的版本,並檢查 ,再執行任何重要工作。

舉例來說,在上述程式碼中,如果指定的 Service Worker 版本 messageSW() 呼叫比預期版本舊,可以直接等候 直到有更新 (呼叫 register() 時會發生這種狀況)。在 通知使用者或更新相關資訊;您也可以手動 跳過等候階段 即可立即啟用新的 Service Worker。

先快取

相對於優先提供網頁快取 首先,您會發現 頁面版本一開始會與 Service Worker (因為系統會提供內容)。因此,這個問題安全無虞 ,立即使用 messageSW()

不過,如果找到並啟用 Service Worker 的更新版本, 當您的網頁呼叫 register() (也就是刻意略過等待階段) 時, 已面臨危險,因此傳送訊息也會不夠安全。

管理這種可能性的方法之一,就是採用 可讓您區分破壞性更新和非破壞性更新。 若是發生破壞性更新,則可以直接告知 Service Worker。建議您警告使用者他們正在執行舊裝置 的網頁版本,並建議他們重新載入頁面以更新。

略過等待小幫手

視窗連線至 Service Worker 訊息的常見慣例是傳送 {type: 'SKIP_WAITING'} 訊息,指示已安裝至應用程式的 Service Worker 跳過等待階段 然後啟動

自 Workbox v6 起,messageSkipWaiting() 方法可用來傳送 {type: 'SKIP_WAITING'} 訊息傳送給與 目前的 Service Worker 註冊。這項功能會在排除 等待 Service Worker。

類型

Workbox

類別,協助處理 Service Worker 的註冊、更新和 Service Worker 生命週期事件的回應。

屬性

  • 建構函式

    void

    使用指令碼網址和 Service Worker 建立新的 Workbox 執行個體 只要設定成「自動重新啟動」 和「在主機維護期間」選項即可指令碼網址和選項與 呼叫 navigator.serviceWorker.register(scriptURL, options)

    constructor 函式如下所示:

    (scriptURL: string | TrustedScriptURL, registerOptions?: object) => {...}

    • scriptURL

      string |TrustedScriptURL

      Service Worker 指令碼 與這個執行個體建立關聯使用 支援 TrustedScriptURL

    • registerOptions

      物件 optional

  • 已啟用

    Promise&lt;ServiceWorker&gt;

  • 控制

    Promise&lt;ServiceWorker&gt;

  • getSW

    void

    使用符合指令碼網址的 Service Worker 參照進行解析 並在執行個體可用後立即執行

    如果註冊時已有啟用中的或等待服務 具有相符指令碼網址的 worker 便會使用 (等待 Service Worker 會優先於使用中的 Service Worker,如果兩者 比對內容。因為等待服務工作處理程序可能登錄更多 )。 如果註冊時沒有相符的有效或等待服務工作人員 但直到找到更新並開始之後,承諾才會解決 安裝,此時會使用安裝服務工作站。

    getSW 函式如下所示:

    () => {...}

    • returns

      Promise&lt;ServiceWorker&gt;

  • messageSW

    void

    將傳遞的資料物件傳送至由此類別註冊的 Service Worker 執行個體 (透過 workbox-window.Workbox#getSW) 並解析 回應 (如有)。

    回應可在 Service Worker 的訊息處理常式中設定 呼叫 event.ports[0].postMessage(...),即可解決 由 messageSW() 傳回。如未設定回應,承諾就永遠不會 解析。

    messageSW 函式如下所示:

    (data: object) => {...}

    • 資料

      物件

      要傳送至 Service Worker 的物件

    • returns

      承諾<任何>

  • messageSkipWaiting

    void

    傳送 {type: 'SKIP_WAITING'} 訊息給 目前處於與目前的註冊相關聯的waiting狀態。

    如果目前沒有註冊,或沒有 Service Worker 為 waiting, 呼叫此方法不會有任何效果。

    messageSkipWaiting 函式如下所示:

    () => {...}

  • register

    void

    為這個執行個體指令碼網址和服務註冊 Service Worker 工作站選項。這個方法預設會將註冊延遲至下列日期之後: 視窗已經載入。

    register 函式如下所示:

    (options?: object) => {...}

    • 選項

      物件 optional

      • 立即

        布林值 選填

    • returns

      Promise&lt;ServiceWorkerRegistration&gt;

  • update

    void

    檢查已註冊的 Service Worker 的更新。

    update 函式如下所示:

    () => {...}

    • returns

      承諾<void>

WorkboxEventMap

WorkboxLifecycleEvent

屬性

  • isExternal

    布林值 選填

  • isUpdate

    布林值 選填

  • originalEvent

    事件 (選填)

  • sw

    ServiceWorker 選用

  • 目標

    WorkboxEventTarget (選用)

  • 類型

    typeOperator

WorkboxLifecycleEventMap

WorkboxLifecycleWaitingEvent

屬性

  • isExternal

    布林值 選填

  • isUpdate

    布林值 選填

  • originalEvent

    事件 (選填)

  • sw

    ServiceWorker 選用

  • 目標

    WorkboxEventTarget (選用)

  • 類型

    typeOperator

  • wasWaitingBeforeRegister

    布林值 選填

WorkboxMessageEvent

屬性

  • 資料

    不限

  • isExternal

    布林值 選填

  • originalEvent

    活動

  • ports

    typeOperator

  • sw

    ServiceWorker 選用

  • 目標

    WorkboxEventTarget (選用)

  • 類型

    "訊息"

方法

messageSW()

workbox-window.messageSW(
  sw: ServiceWorker,
  data: object,
)

這個外掛程式會透過 postMessage 將資料物件傳送至 Service Worker,並解析 回應 (如有)。

回應可在 Service Worker 的訊息處理常式中設定 呼叫 event.ports[0].postMessage(...),即可解決 由 messageSW() 傳回。如未設定回應,承諾就不會 解析。

參數

  • sw

    ServiceWorker

    要接收訊息的 Service Worker。

  • 資料

    物件

    要傳送至 Service Worker 的物件。

傳回

  • 承諾<任何>