使用 Workbox 构建弹性搜索体验

此代码实验室向您展示如何使用 Workbox 实现弹性搜索体验。它使用的演示应用包含一个搜索框,该搜索框调用服务器端点,并将用户重定向到基本的 HTML 页面。

测量

在添加优化之前,最好先分析应用程序的当前状态。

  • 点击 Remix to Edit(混音进行编辑) 使项目可编辑。
  • 要预览网站,请按 View App(查看应用)。然后按 Fullscreen(全屏) fullscreen

在新打开的标签页中,检查网站在离线时的行为

  1. 按 `Control+Shift+J`(在 Mac 上按 `Command+Option+J`)打开开发者工具。
  2. 点击 Network(网络) 选项卡。
  3. 打开 Chrome 开发者工具并选择 Network(网络)面板。
  4. Throttling(节流)下拉列表中,选择 Offline(离线)
  5. 在演示应用中输入搜索查询,然后点击 Search(搜索) 按钮。

显示标准的浏览器错误页面

A screenshot of the default offline UX in the browser.

提供回退响应

Service Worker 包含将离线页面添加到 预缓存列表 的代码,因此始终可以在 Service Worker install 事件中缓存该页面。

通常,您需要在构建时指示 Workbox 将此文件添加到预缓存列表,方法是将该库与您选择的构建工具(例如 webpackgulp)集成。

为了简单起见,我们已经为您完成了此操作。public/sw.js 中的以下代码执行此操作

const FALLBACK_HTML_URL = '/index_offline.html';

workbox.precaching.precacheAndRoute([FALLBACK_HTML_URL]);

接下来,添加代码以使用离线页面作为回退响应

  1. 要查看源代码,请按 View Source(查看源代码)
  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();
  }
});

该代码执行以下操作

  • 定义一个默认的 Network Only(仅网络)策略,该策略将应用于所有请求。
  • 通过调用 workbox.routing.setCatchHandler() 声明全局错误处理程序来管理失败的请求。当请求用于文档时,将返回回退离线 HTML 页面。

要测试此功能

  1. 返回到运行您的应用的另一个标签页。
  2. Throttling(节流) 下拉列表设置回 Online(在线)
  3. 按 Chrome 的 Back(后退) 按钮导航回搜索页面。
  4. 确保开发者工具中的 Disable cache(禁用缓存) 复选框处于未选中状态。
  5. 长按 Chrome 的 Reload(重新加载) 按钮并选择 Empty cache and hard reload(清空缓存并硬性重新加载) 以确保您的 Service Worker 已更新。
  6. Throttling(节流) 下拉列表再次设置回 Offline(离线)
  7. 输入搜索查询,然后再次点击 Search(搜索) 按钮。

显示回退 HTML 页面

A screenshot of the custom offline UX in the browser.

请求通知权限

为简单起见,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(Workbox 后台同步) 以持久化离线查询,以便在浏览器检测到连接已恢复时可以重试这些查询。

  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 查询参数,以便在用户点击通知时可以选取此缓存条目。

要将后台同步与您的服务集成,请为对搜索 URL (/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. Throttling(节流) 下拉列表设置回 Online(在线)
  3. 按 Chrome 的 Back(后退) 按钮导航回搜索页面。
  4. 长按 Chrome 的 Reload(重新加载) 按钮并选择 Empty cache and hard reload(清空缓存并硬性重新加载) 以确保您的 Service Worker 已更新。
  5. Throttling(节流) 下拉列表再次设置回 Offline(离线)
  6. 输入搜索查询,然后再次点击 Search(搜索) 按钮。
  7. 点击 subscribe to notifications(订阅通知)
  8. 当 Chrome 询问您是否要授予应用发送通知的权限时,点击 Allow(允许)
  9. 输入另一个搜索查询,然后再次点击 Search(搜索) 按钮。
  10. Throttling(节流) 下拉列表再次设置回 Online(在线)

连接恢复后,将显示通知

A screenshot of the full offline flow.

结论

Workbox 提供了许多内置功能,使您的 PWA 更加弹性和引人入胜。在本代码实验室中,您探索了如何通过 Workbox 抽象实现 Background Sync API,以确保离线用户查询不会丢失,并且可以在连接恢复后重试。演示是一个简单的搜索应用,但您可以将类似的实现用于更复杂的场景和用例,包括聊天应用、在社交网络上发布消息等。