school Are you a developer in an agency in the UK, Indonesia or India? Find out more about our free 2 day Progressive Web Apps training .

在网络应用中添加服务工作线程和离线功能

概览

9246b0abd8d860da.png

在此代码实验室中,您将学习如何将服务工作线程集成到现有应用内,以使应用能够离线工作。该应用名为 Air Horner。点击喇叭就会发声。

您将学习的内容

  • 如何向现有项目添加基础服务工作线程。
  • 如何使用 Chrome DevTools 模拟离线模式以及检查和调试服务工作线程。
  • 一种简单的离线缓存策略。

您需具备的条件

  • Chrome 52 或更高版本。
  • Promises、Git 和 Chrome DevTools 的基本了解。
  • 示例代码。
  • 文本编辑器。
  • 本地网络服务器。如果您想要使用此代码实验室中所述的网络服务器,则需要在命令行中安装 Python。

获取示例代码

通过 SSH 从命令行克隆 GitHub 存储区。

$ git clone git@github.com:GoogleChrome/airhorn.git

或 HTTPS:

$ git clone https://github.com/GoogleChrome/airhorn.git

运行应用示例

首先,我们先看看应用示例的最终样子(提示:太奇妙了)。

通过查看 master 分支确保您位于正确的(最终)分支。

$ git checkout master

从本地网络服务器运行网站。您可以使用任意网络服务器,但对于此代码实验室的其他部分,我们假定您在端口 3000 上使用的是 Python 的 SimpleHTTPServer,以便从 localhost:3000 中运行应用。

$ cd app
$ python -m SimpleHTTPServer 3000

在 Chrome 中打开网站。您会看到:9246b0abd8d860da.png

测试应用

点击喇叭,应能发声。

现在,您可以使用 Chrome DevTools 模拟离线模式了。

打开 DevTools,转至 Application 面板,然后启用 Offline 复选框。在下面的屏幕截图中,鼠标悬停在复选框上。

479219dc5f6ea4eb.png

点击复选框后,请注意 Network 面板标签旁边的警告图标(带有感叹号的黄色三角形)。这表示您处于离线状态。

如需证明您处于离线模式,请转至 https://google.com。您会看到 Chrome 的“there is no Internet connection”错误消息。

现在,返回到应用中。尽管您处于离线状态,页面应仍然能够完全重新加载。您应仍然能够使用喇叭。

它能够离线工作的原因就是此 代码实验室的基础:通过服务工作线程提供离线支持。

构建初学者应用

您现在将要删除应用中的所有离线支持,学习如何使用服务工作线程重新将离线支持添加到应用中

请查看应用的“断开”版本,此版本未实现服务工作线程。

$ git checkout code-lab

返回到 DevTools 的 Application 面板,禁用 Offline 复选框,以便重新返回在线状态。

运行页面。应用应能如期运行。

现在,使用 DevTools 重新模拟离线模式(通过在 Application 面板中启用 Offline 复选框)。__注意!如果您不是非常了解服务工作线程,则会看到一些异常行为。

您可能会看到什么?因为您处于离线状态,并且这个版本的应用没有服务工作线程,您将看到 Chrome 中显示典型的“there is no Internet connection”错误消息。

但您实际看到的是...功能完备的离线应用!

9246b0abd8d860da.png

这是怎么回事?回想一下您在开始此代码实验室时的情景,您尝试了应用的完整版本。当您运行那个版本时,应用实际上安装了服务工作线程。现在,在您每次运行应用时,服务工作线程都会自动运行。一旦 localhost:3000 等作用域(您将会在下一部分中了解有关作用域的更多内容)中安装了服务工作线程,服务工作线程会在您每次访问作用域时自动启动,除非您以编程方式或手动将其删除。

如需修复这一问题,请转至 DevTools 的 Application 面板,点击 Service Workers 选项卡,然后点击 Unregister 按钮。在下面的屏幕截图中,鼠标悬停在按钮上。

837b46360756810a.png

现在,在您重新加载网站之前,请确保您仍然在使用 DevTools 模拟离线模式。重新加载页面,应会如期显示“there is no Internet connection”错误消息。

da11a350ed38ad2e.png

在网站上注册服务工作线程

现在,可以将离线支持重新添加到应用中。这个过程由两个步骤组成:

  1. 创建一个将作为服务工作线程的 JavaScript 文件。
  2. 指示浏览器将此 JavaScript 文件注册为“服务工作线程”。

首先,创建一个名为 sw.js 的空白文件,然后将其放入 /app 文件夹。

现在打开 index.html,并将以下代码添加到 <body> 底部。

<script>
if('serviceWorker' in navigator) {
  navigator.serviceWorker
           .register('/sw.js')
           .then(function() { console.log("Service Worker Registered"); });
}
</script>

脚本会检查浏览器是否支持服务工作线程。如果不支持,它会将我们当前使用的空白文件 sw.js 注册为服务工作线程,然后记录到控制台。

在重新运行网站之前,返回到 DevTools,查看 Application 面板的 Service Workers 标签。此标签当前应为空,表示网站没有安装服务工作线程。

37d374c4b51d273.png

确保已停用 DevTools 中的 Offline 复选框。重新加载页面。在加载页面时,您可以看到服务工作线程已经完成注册。

b9af9805d4535bd3.png

Source 标签旁边,您可以看到已注册的服务工作线程源代码的链接。

3519a5068bc773ea.png

如果您想要检查当前为页面安装的服务工作线程,请点击链接。这将会在 DevTools 的 Sources 面板中为您显示服务工作线程的源代码。例如,现在点击链接,您会看到一个空文件。

dbc14cbb8ca35312.png

安装网站资产

注册服务工作线程后,当用户首次点击页面时,会触发 install 事件。此事件就是您要缓存页面资产的地方。

将以下代码添加到 sw.js。

importScripts('/cache-polyfill.js');

self.addEventListener('install', function(e) {
 e.waitUntil(
   caches.open('airhorner').then(function(cache) {
     return cache.addAll([
       '/',
       '/index.html',
       '/index.html?homescreen=1',
       '/?homescreen=1',
       '/styles/main.css',
       '/scripts/main.min.js',
       '/sounds/airhorn.mp3'
     ]);
   })
 );
});

第一行会添加缓存 polyfill。此 polyfill 已经添加到存储区。我们需要使用 polyfill 是因为 Cache API 尚未在所有浏览器中得到完全支持。接下来是 install 事件侦听器。install 事件侦听器可以打开 caches 对象,然后使用我们要缓存的资源列表进行填充。关于 addAll 操作的一个重要事情就是要么全部添加,要么全部不添加。如果其中有一个文件不存在或无法抓取,整个 addAll 操作将会失败。合格的应用将会处理这种情况。

下一步是对服务工作线程编程,以将任意资源的请求返回给拦截,并使用 caches 对象返回每个资源的本地存储版本。

拦截网页请求

服务工作线程的一个强大的功能就是,一旦它控制页面,就可以拦截页面发出的每个请求,并确定对请求执行的操作。在本部分中,您将对服务工作线程进行编程,以拦截请求并返回缓存版本的资产,而不是到网络上检索这些资产。

第一步是将一个事件处理程序附加到 fetch 事件。发出的每个请求都会触发此事件。

将以下代码添加到 sw.js 的底部,以便记录父页面发出的请求。

我们来测试一下这个功能。注意!您将会看到更加异常的服务工作线程行为。

打开 DevTools,转至 Application 面板。应停用 Offline 复选框。按 Esc 键以打开 DevTools 窗口底部的 Console 抽屉。您的 DevTools 窗口应类似于以下屏幕截图:

c96de824be6852d7.png

现在重新加载页面并查看 DevTools 窗口。首先,我们预期能看到记录到控制台中的大量请求,但没有看到。其次,在 Service Worker 窗格中,我们可以看到 Status 已发生更改:

c7cfb6099e79d5aa.png

Status 中,有一个新的服务工作线程正等待激活。这就是包含我们刚才所做更改的新的服务工作线程。因此,出于某种原因,我们以前安装的旧的服务工作线程(空白文件)仍然在控制页面。如果您点击 Source 旁边的 sw.js 链接,便可验证旧的服务工作线程仍然在运行中。

如需修复这种不便,请启用 Update on reload 复选框。

26f2ae9a805bc69b.png

启用此复选框后,DevTools 会始终在每个页面重新加载时更新服务工作线程。这在主动开发服务工作线程时非常有用。

现在重新加载页面,就会看到系统安装了新的服务工作线程,并且正在将请求网址记录到控制器,如预期一样。

53c23650b131143a.png

现在,您需要确定使用这些请求要完成的任务。默认情况下,如果您未进行任何设置,请求会传递到网络,系统会将响应返回到网页。

要使应用离线工作,如果缓存中存在请求,我们需要从中获取请求。

请更新您的抓取事件侦听器,以匹配以下代码。

event.respondWith() 方法会让浏览器评估未来事件的结果。caches.match(event.request) 会获取触发抓取事件的当前网络请求,在缓存中寻找匹配的资源。匹配通过查找网址字符串执行。match 方法会返回可解析的 promise,即使未在缓存中找到相关文件。这意味着您可以选择要执行的操作。在您的简单案例中,如果未找到文件,您会想要从网络中 fetch 它,然后将其返回到浏览器。

这是最简单的情况,还有许多其他缓存情境。例如,您可以增量方式缓存之前未缓存请求的所有响应,以便以后从缓存返回这些响应。

恭喜!

现在您获得离线支持了。在您处于在线状态时重新加载页面,将服务工作线程更新为最新版本,然后使用 DevTools 转至离线模式。重新加载页面,就可以拥有功能完备的离线汽笛了!

我们已经阐述的内容

  • 如何向现有项目添加基础服务工作线程。
  • 如何使用 Chrome DevTools 模拟离线模式以及检查和调试服务工作线程。
  • 一种简单的离线缓存策略。

后续步骤

了解详情

发现问题,或者有反馈?

立即提交问题,帮助我们让代码实验室更加强大。谢谢!