利用 Workbox 打造弹性搜索体验

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

测量

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

  • 点击 Remix to Edit 使项目可修改。
  • 如需预览网站,请按查看应用,然后按全屏 全屏

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

  1. 按 `Control+Shift+J`(在 Mac 上,则按 `Command+Option+J`)打开开发者工具。
  2. 点击网络标签页。
  3. 打开 Chrome 开发者工具,然后选择“Network”面板。
  4. 限制下拉列表中,选择离线
  5. 在演示版应用中,输入搜索查询,然后点击 Search 按钮。

标准浏览器错误页面如下所示:

浏览器中默认离线用户体验的屏幕截图。

提供回退响应

Service Worker 包含将离线网页添加到预缓存列表中的代码,因此该网页始终可以缓存在 Service Worker 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. Throttling 下拉列表重新设为 Online(在线)。
  3. 按 Chrome 的返回按钮可返回到搜索页面。
  4. 确保已停用开发者工具中的 Disable cache 复选框。
  5. 长按 Chrome 的重新加载按钮,然后选择 Empty cache and 硬编码,以确保您的 Service Worker 得到更新。
  6. 限制下拉列表重新设置为离线
  7. 输入搜索查询,然后再次点击搜索按钮。

后备 HTML 网页如下所示:

浏览器中自定义离线用户体验的屏幕截图。

请求通知权限

为简单起见,views/index_offline.html 处的离线页面已包含在底部的脚本块中请求通知权限的代码:

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

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

代码会执行以下操作:

  • 当用户点击订阅通知时,系统会调用 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. Throttling 下拉列表重新设为 Online(在线)。
  3. 按 Chrome 的返回按钮可返回到搜索页面。
  4. 长按 Chrome 的重新加载按钮,然后选择 Empty cache and 硬编码,以确保您的 Service Worker 得到更新。
  5. 限制下拉列表重新设置为离线
  6. 输入搜索查询,然后再次点击搜索按钮。
  7. 点击订阅通知
  8. 当 Chrome 询问您是否授予该应用发送通知的权限时,请点击允许
  9. 输入其他搜索查询,然后再次点击搜索按钮。
  10. Throttling 下拉列表重新设置为 Online(在线)。

连接恢复后,系统会显示通知:

完整离线流程的屏幕截图。

总结

Workbox 提供许多内置功能,可让您 PWA 更具弹性和吸引力。 在此 Codelab 中,您探索了如何通过 Workbox 抽象实现 Background Sync API,以确保离线用户查询不会丢失,并且可以在连接恢复后重试。 该演示是一款简单的搜索应用,但您可以针对更复杂的场景和用例(包括聊天应用、在社交网络上发布消息等)使用类似实现。