Workbox-background-sync

當您將資料傳送至網路伺服器時,有時要求將會失敗。這可能是因為使用者已中斷連線,或是伺服器停止運作。無論是您經常要嘗試再次傳送要求,都不成問題。

新的 BackgroundSync API 是解決這個問題的理想解決方案。Service Worker 偵測到網路要求失敗時,可註冊接收 sync 事件,當瀏覽器認為連線已傳回時,就會傳送事件。請注意,即便使用者已離開應用程式,也可以傳送同步處理事件,這樣比重試失敗要求的傳統方法有效。

Workbox Background Sync 的目標是更容易使用 BackgroundSync API,並與其他 Workbox 模組整合。並針對尚未實作 BackgroundSync 的瀏覽器導入備用策略。

支援 BackgroundSync API 的瀏覽器會由瀏覽器管理的間隔代您自動重播失敗的要求,可能在嘗試重播之間採用指數輪詢策略。在不支援 BackgroundSync API 的瀏覽器中,Workbox Background Sync 會在服務工作站啟動時自動嘗試重播。

基本用法

使用 Background Sync 時,最簡單的方法是使用 Plugin,將失敗的要求自動排入佇列,並在日後觸發 sync 事件時重試。

import {BackgroundSyncPlugin} from 'workbox-background-sync';
import {registerRoute} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';

const bgSyncPlugin = new BackgroundSyncPlugin('myQueueName', {
  maxRetentionTime: 24 * 60, // Retry for max of 24 Hours (specified in minutes)
});

registerRoute(
  /\/api\/.*\/*.json/,
  new NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
  'POST'
);

BackgroundSyncPlugin 會掛鉤 fetchDidFail 外掛程式回呼,而 fetchDidFail 只會在發生例外狀況時叫用,很可能是網路故障。也就是說,如果收到的回應具有 4xx5xx 錯誤狀態,系統就不會重試要求。如要重試所有產生的要求 (例如 5xx 狀態),您可以在策略中新增 fetchDidSucceed 外掛程式

const statusPlugin = {
  fetchDidSucceed: ({response}) => {
    if (response.status >= 500) {
      // Throwing anything here will trigger fetchDidFail.
      throw new Error('Server error.');
    }
    // If it's not 5xx, use the response as-is.
    return response;
  },
};

// Add statusPlugin to the plugins array in your strategy.

進階用法

Workbox Background Sync 也提供 Queue 類別,讓您執行個體化和新增失敗的要求。失敗的要求會儲存在 IndexedDB 中,如果瀏覽器認為連線恢復 (也就是收到同步事件時),就會重試。

建立佇列

如要建立 Workbox 背景同步佇列,您必須使用佇列名稱建構這個佇列 (此佇列不得在您的來源中具有唯一性):

import {Queue} from 'workbox-background-sync';

const queue = new Queue('myQueueName');

佇列名稱會做為標記名稱的一部分,由全域 SyncManager 比對 register()。同時也是索引資料庫資料庫的物件存放區名稱。

將要求新增至佇列

建立佇列執行個體後,您可以為該執行個體新增失敗的要求,您可以透過叫用 .pushRequest() 方法新增失敗的要求。舉例來說,以下程式碼會擷取任何失敗的要求並新增至佇列:

import {Queue} from 'workbox-background-sync';

const queue = new Queue('myQueueName');

self.addEventListener('fetch', event => {
  // Add in your own criteria here to return early if this
  // isn't a request that should use background sync.
  if (event.request.method !== 'POST') {
    return;
  }

  const bgSyncLogic = async () => {
    try {
      const response = await fetch(event.request.clone());
      return response;
    } catch (error) {
      await queue.pushRequest({request: event.request});
      return error;
    }
  };

  event.respondWith(bgSyncLogic());
});

將連線新增至佇列後,服務工作站收到 sync 事件 (當瀏覽器認為連線已恢復時) 時,就會自動重試要求。不支援 BackgroundSync API 的瀏覽器會在每次啟動服務工作站時重試佇列。這項操作需要使用控管服務工作站的頁面,因此不太有效。

測試 Workbox 背景同步處理

很抱歉,測試 BackgroundSync 的原因有很多種。

如要測試導入作業,建議您執行下列操作:

  1. 載入頁面並註冊 Service Worker。
  2. 關閉電腦的網路或關閉網路伺服器。
    • 請勿使用 CHROME DEVTOOLS 離線工具。開發人員工具中的離線核取方塊只會影響來自網頁的要求。Service Worker 要求會繼續傳送。
  3. 發出在 Workbox Background Sync 中排入佇列的網路要求。
    • 如要查看要求已排入佇列,請查看 Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests
  4. 開啟你的網路或網路伺服器。
  5. 強制執行早期 sync 事件,方法是前往 Chrome DevTools > Application > Service Workers,輸入 workbox-background-sync:<your queue name> 的標記名稱,其中 <your queue name> 應為您設定的佇列名稱,然後按一下「Sync」按鈕。

    Chrome 開發人員工具中的「同步處理」按鈕範例

  6. 對於失敗的要求,您應該會看到網路要求成功通過,而 IndexedDB 資料現在應該是空白的,因為要求已成功重新播放。

類型

BackgroundSyncPlugin

實作 fetchDidFail 生命週期回呼的類別。這樣就能更輕鬆地將失敗的要求新增至背景同步佇列。

屬性

Queue

用於管理在 IndexedDB 中儲存失敗要求的類別,並於稍後重試。所有儲存及重播程序都可以透過回呼觀測。

屬性

  • 建構函式

    void

    使用指定的選項建立佇列的執行個體

    constructor 函式如下所示:

    (name: string,options?: QueueOptions)=> {...}

    • 名稱

      字串

      這個佇列的專屬名稱。此名稱不得重複,因為此名稱是用來註冊同步事件,以及將要求儲存在這個執行個體專屬的索引資料庫。如果偵測到重複的名稱,就會擲回錯誤。

    • 選項

      QueueOptions 選用

  • 名稱

    字串

  • getAll

    void

    maxRetentionTime 傳回所有未過期的項目。任何過期的項目都會從佇列中移除。

    getAll 函式如下所示:

    ()=> {...}

    • returns

      Promise<QueueEntry[]>

  • popRequest

    void

    移除並傳回佇列中的最後一個要求 (以及要求的時間戳記和任何中繼資料)。傳回的物件格式為:{request, timestamp, metadata}

    popRequest 函式如下所示:

    ()=> {...}

    • returns

      Promise<QueueEntry>

  • pushRequest

    void

    將傳遞的要求儲存在佇列結尾的 IndexedDB 中 (含時間戳記和任何中繼資料)。

    pushRequest 函式如下所示:

    (entry: QueueEntry)=> {...}

    • 項目

      QueueEntry

    • returns

      Promise<void>

  • registerSync

    void

    使用這個執行個體專屬的代碼註冊同步處理事件。

    registerSync 函式如下所示:

    ()=> {...}

    • returns

      Promise<void>

  • replayRequests

    void

    循環處理佇列中的每個要求,並嘗試重新擷取。如有任何要求無法重新擷取,系統會將該要求排入佇列中的相同位置 (這會註冊下一個同步事件的重試作業)。

    replayRequests 函式如下所示:

    ()=> {...}

    • returns

      Promise<void>

  • shiftRequest

    void

    移除並傳回佇列中的第一個要求 (以及要求的時間戳記和任何中繼資料)。傳回的物件格式為:{request, timestamp, metadata}

    shiftRequest 函式如下所示:

    ()=> {...}

    • returns

      Promise<QueueEntry>

  • 大小

    void

    傳回佇列中出現的項目數量。請注意,過期項目 (每 maxRetentionTime) 也會計入這項計數。

    size 函式如下所示:

    ()=> {...}

    • returns

      Promise<number>

  • unshiftRequest

    void

    將傳遞的要求儲存在佇列開頭的 IndexedDB 中 (含時間戳記和任何中繼資料)。

    unshiftRequest 函式如下所示:

    (entry: QueueEntry)=> {...}

    • 項目

      QueueEntry

    • returns

      Promise<void>

QueueOptions

屬性

  • forceSyncFallback

    布林值 (選用)

  • maxRetentionTime

    數字 選填

  • onSync

    OnSyncCallback 選用

QueueStore

用於管理在 IndexedDB 中儲存佇列要求的類別,該類別會依據佇列名稱建立索引,以便輕鬆存取。

大多數開發人員不需要直接存取此類別,此類別可用於進階用途。

屬性

  • 建構函式

    void

    建立此執行個體與佇列執行個體的關聯,以便使用其佇列名稱來識別新增的項目。

    constructor 函式如下所示:

    (queueName: string)=> {...}

    • queueName

      字串

  • deleteEntry

    void

    刪除指定 ID 的項目。

    警告:這個方法並不會確保已刪除的項目屬於這個佇列 (也就是與 queueName 相符)。不過,由於這個類別不會公開公開,因此可以接受這項限制。如果另外進行檢查,這個方法會速度過慢。

    deleteEntry 函式如下所示:

    (id: number)=> {...}

    • id

      號碼

    • returns

      Promise<void>

  • getAll

    void

    傳回儲存庫中符合 queueName 的所有項目。

    getAll 函式如下所示:

    ()=> {...}

    • returns

      Promise<QueueStoreEntry[]>

  • popEntry

    void

    移除並傳回佇列中符合 queueName 的最後一個項目。

    popEntry 函式如下所示:

    ()=> {...}

    • returns

      Promise<QueueStoreEntry>

  • pushEntry

    void

    附加佇列的最後一項項目。

    pushEntry 函式如下所示:

    (entry: UnidentifiedQueueStoreEntry)=> {...}

    • 項目

      UnidentifiedQueueStoreEntry

    • returns

      Promise<void>

  • shiftEntry

    void

    移除並傳回佇列中符合 queueName 的第一個項目。

    shiftEntry 函式如下所示:

    ()=> {...}

    • returns

      Promise<QueueStoreEntry>

  • 大小

    void

    傳回商店中符合 queueName 的項目數量。

    size 函式如下所示:

    ()=> {...}

    • returns

      Promise<number>

  • unshiftEntry

    void

    請先在佇列中前置一個項目。

    unshiftEntry 函式如下所示:

    (entry: UnidentifiedQueueStoreEntry)=> {...}

    • 項目

      UnidentifiedQueueStoreEntry

    • returns

      Promise<void>

StorableRequest

可輕鬆將要求序列化和取消序列化的類別,以便儲存在 IndexedDB 中。

大多數開發人員不需要直接存取此類別,此類別可用於進階用途。

屬性

  • 建構函式

    void

    接受要求資料物件,該物件可用來建構 Request,但也可以儲存在索引資料庫。

    constructor 函式如下所示:

    (requestData: RequestData)=> {...}

    • requestData

      RequestData

      要求資料物件,包含 url 加上 [requestInit]https://fetch.spec.whatwg.org/#requestinit 的任何相關屬性。

  • clone

    void

    建立並傳回執行個體的深度本機副本。

    clone 函式如下所示:

    ()=> {...}

  • toObject

    void

    傳回例項 _requestData 物件的深度本機副本。

    toObject 函式如下所示:

    ()=> {...}

    • returns

      RequestData

  • toRequest

    void

    將這個執行個體轉換為要求。

    toRequest 函式如下所示:

    ()=> {...}

    • returns

      要求

  • fromRequest

    void

    將要求物件轉換為可複製或建立 JSON 字串的純物件。

    fromRequest 函式如下所示:

    (request: Request)=> {...}

    • 申請。

      要求