透過 Workbox 打造彈性的搜尋體驗

Demián Renzulli
Demián Renzulli

本程式碼研究室會說明如何使用 Workbox 實作彈性搜尋體驗。這個應用程式包含一個會呼叫伺服器端點的搜尋方塊,並將使用者重新導向至基本 HTML 網頁。

測量

新增最佳化項目之前,建議您先分析應用程式的目前狀態。

  • 按一下「Remix to Edit」,即可編輯專案。
  • 如要預覽網站,請按下「查看應用程式」,然後按下「全螢幕」圖示 全螢幕

在新開啟的分頁中,檢查網站離線時的行為:

  1. 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
  2. 按一下 [網路] 分頁標籤。
  3. 開啟 Chrome 開發人員工具,然後選取「網路」面板。
  4. 在「節流」下拉式清單中,選取「離線」
  5. 在示範應用程式中輸入搜尋查詢,然後按一下「搜尋」按鈕。

系統會顯示標準瀏覽器錯誤頁面:

瀏覽器中預設離線 UX 的螢幕截圖。

提供備用回覆

服務工作人員包含將離線網頁新增至預先快取清單的程式碼,因此一律可在服務工作人員 install 事件中快取。

通常您需要指示 Workbox 在建構時將這個檔案新增至預先快取清單,方法是將程式庫與所選建構工具 (例如 webpackgulp) 整合。

為簡化程序,我們已為您完成這項作業。public/sw.js 中的下列程式碼會執行這項操作:

const FALLBACK_HTML_URL = '/index_offline.html';

workbox.precaching.precacheAndRoute([FALLBACK_HTML_URL]);

接著,請新增程式碼,將離線網頁做為備用回應:

  1. 如要查看來源,請按「查看來源」
  2. public/sw.js 底部新增下列程式碼:
workbox.routing.setDefaultHandler(new workbox.strategies.NetworkOnly());

workbox.routing.setCatchHandler(({event}) => {
  switch (event.request.destination) {
    case 'document':
      return caches.match(FALLBACK_HTML_URL);
      break;
    default:
      return Response.error();
  }
});

該程式碼會執行以下作業:

  • 定義預設的「僅限聯播網」策略,適用於所有要求。
  • 呼叫 workbox.routing.setCatchHandler() 宣告全域錯誤處理常式,以管理失敗的要求。如果要求的是文件,系統會傳回備援的離線 HTML 網頁。

如要測試這項功能,請按照下列步驟操作:

  1. 返回執行應用程式的其他分頁。
  2. 將「節流」下拉式清單設回「線上」
  3. 按下 Chrome 的「返回」按鈕,返回搜尋頁面。
  4. 確認開發人員工具中的「停用快取」核取方塊已停用。
  5. 長按 Chrome 的「重新載入」按鈕,然後選取「清空快取並強制重新載入」,確保服務工作人員已更新。
  6. 將「Throttling」下拉式清單再次設為「Offline」
  7. 輸入搜尋查詢,然後再次按一下「搜尋」按鈕。

系統會顯示備用 HTML 網頁:

瀏覽器中自訂離線 UX 的螢幕截圖。

要求通知權限

為求簡單,views/index_offline.html 的離線網頁底部已包含程式碼,可在指令碼區塊中要求通知權限:

function requestNotificationPermission(event) {
  event.preventDefault();

  Notification.requestPermission().then(function (result) {
    showOfflineText(result);
  });
}

該程式碼會執行以下作業:

  • 使用者點選「subscribe to notifications」時,系統會呼叫 requestNotificationPermission() 函式,該函式會呼叫 Notification.requestPermission(),顯示預設的瀏覽器權限提示。這項 Promise 會以使用者選取的權限 (granteddenieddefault) 解析。
  • 將已解決的權限傳遞至 showOfflineText(),向使用者顯示適當的文字。

保留離線查詢,並在連上網路後重試

接著,請實作 Workbox Background Sync,以便保留離線查詢,讓瀏覽器在偵測到連線恢復時重試查詢。

  1. 開啟 public/sw.js 進行編輯。
  2. 在檔案結尾新增下列程式碼:
const bgSyncPlugin = new workbox.backgroundSync.Plugin('offlineQueryQueue', {
  maxRetentionTime: 60,
  onSync: async ({queue}) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        const response = await fetch(entry.request);
        const cache = await caches.open('offline-search-responses');
        const offlineUrl = `${entry.request.url}&notification=true`;
        cache.put(offlineUrl, response);
        showNotification(offlineUrl);
      } catch (error) {
        await this.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

該程式碼會執行以下作業:

  • workbox.backgroundSync.Plugin 包含將失敗要求加入佇列的邏輯,以便稍後重試。這些要求會保留在 IndexedDB 中。
  • maxRetentionTime 表示要求可能重試的時間長度。在本例中,我們選擇 60 分鐘 (之後就會捨棄)。
  • onSync 是這段程式碼最重要的部分。連線恢復時,系統會呼叫這個回呼,以便擷取佇列中的要求,然後從網路擷取。
  • 網路回應會新增至 offline-search-responses 快取,並附加 &notification=true 查詢參數,以便使用者點選通知時,系統能擷取這個快取項目。

如要將背景同步處理功能與服務整合,請為對搜尋網址 (/search_action) 的要求定義 NetworkOnly 策略,並傳遞先前定義的 bgSyncPlugin。在 public/sw.js 底部新增下列程式碼:

const matchSearchUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return url.pathname === '/search_action' && !(notificationParam === 'true');
};

workbox.routing.registerRoute(
  matchSearchUrl,
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
);

這會告知 Workbox 一律連上網路,並在要求失敗時使用背景同步邏輯。

接著,在 public/sw.js 底部新增下列程式碼,為來自通知的要求定義快取策略。使用 CacheFirst 策略,以便從快取提供這些資源。

const matchNotificationUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return (url.pathname === '/search_action' && (notificationParam === 'true'));
};

workbox.routing.registerRoute(matchNotificationUrl,
  new workbox.strategies.CacheFirst({
     cacheName: 'offline-search-responses',
  })
);

最後,請新增程式碼來顯示通知:

function showNotification(notificationUrl) {
  if (Notification.permission) {
     self.registration.showNotification('Your search is ready!', {
        body: 'Click to see you search result',
        icon: '/img/workbox.jpg',
        data: {
           url: notificationUrl
        }
     });
  }
}

self.addEventListener('notificationclick', function(event) {
  event.notification.close();
  event.waitUntil(
     clients.openWindow(event.notification.data.url)
  );
});

測試這項功能

  1. 返回執行應用程式的其他分頁。
  2. 將「節流」下拉式清單設回「線上」
  3. 按下 Chrome 的「返回」按鈕,返回搜尋頁面。
  4. 長按 Chrome 的「重新載入」按鈕,然後選取「清空快取並強制重新載入」,確保服務工作人員已更新。
  5. 將「Throttling」下拉式清單再次設為「Offline」
  6. 輸入搜尋查詢,然後再次按一下「搜尋」按鈕。
  7. 按一下「訂閱通知」
  8. 當 Chrome 詢問是否要授予應用程式傳送通知的權限時,請按一下「允許」
  9. 輸入其他搜尋查詢,然後再次點選「搜尋」按鈕。
  10. 將「節流」下拉式清單再次設為「線上」

連線恢復後,系統會顯示通知:

完整離線流程的螢幕截圖。

結論

Workbox 提供許多內建功能,可讓 PWA 更具韌性及吸引力。在本程式碼研究室中,您已瞭解如何透過 Workbox 抽象化實作 Background Sync API,確保離線使用者查詢不會遺失,並在連線恢復後重試。這個示範是簡單的搜尋應用程式,但您可以將類似的實作方式用於更複雜的情境和用途,包括聊天應用程式、在社群網路上發布訊息等。