logo头像
Snippet 博客主题

移动PWA初探

在去年上海举办的2017谷歌开发者大会上,PWA作为会议的一个重要内容被推介,笔者作为参会嘉宾看了PWA的内容后,觉得这种技术会是未来移动发展的一个趋势。Google开发技术推广工程师Michael Yeung介绍称,新浪微博正在打造一款全新体验的Web Mobile PWA应用,读者可以通过微博提供的PWA版访问网址:m.weibo.cn/beta
这里写图片描述

在当前的移动跨平台开发方案中,主要的技术有PWA和Weex、RN(这个笔者在16年专门进行了研究,并出版了相关的书籍)。不过纵观这些移动技术可以发现,PWA是优化web app,RN是用web调用native思路,weex还是使用web栈调用native的思路。

在移动碎片化严重的当前,如何制定一个统一的标准,才是为了移动技术发展的方向,也就是说:“Web不会趋向于Native,而是Native趋向于Web。”。

PWA简介

PWA全称Progressive Web Apps(渐进式网络应用),该项目由谷歌在2015年主导推出,主要的特性是让Web App的体验能更接近原生应用,显著提高应用加载速度,甚至可以在离线状态下运行,多种手机/PC浏览器已支持加载PWA网页。
这里写图片描述
所谓的P(Progressive)这里有两层含义,一方面是渐进增强,让WEB APP的体验和功能能够用渐进增强的方式来更接近原生APP的体验及功能;另一方面是指下一代WEB技术,PWA并不是描述一个技术,而是一些技术的合集。PWA 本质上是 Web App,借助一些新技术也具备了 Native App 的一些特性,兼具 Web App 和 Native App 的优点。

PWA 的主要特点包括下面三点:

  • 可靠 - 即使在不稳定的网络环境下,也能瞬间加载并展现
  • 体验 - 快速响应,并且有平滑的动画响应用户的操作
  • 粘性 - 像设备上的原生应用,具有沉浸式的用户体验,用户可以添加到桌面

PWA 本身强调渐进式,并不要求一次性达到安全、性能和体验上的所有要求,开发者可以通过 PWA Checklist 查看现有的特征。

PWA特性

下面就从安全、性能和体验三个方面来介绍PWA所具有的特性。

可靠

当用户打开我们站点时(从桌面 icon 或者从浏览器),通过 Service Worker 能够让用户在网络条件很差的情况下也能瞬间加载并且展现。

Service Worker 是用 JavaScript 编写的 JS 文件,能够代理请求,并且能够操作浏览器缓存,通过将缓存的内容直接返回,让请求能够瞬间完成。开发者可以预存储关键文件,可以淘汰过期的文件等等,给用户提供可靠的体验。

更详细的内容可以访问: Service Worker

体验

如果站点加载时间超过 3s,53% 的用户会放弃等待。页面展现之后,用户期望有平滑的体验,过渡动画和快速响应。

为了保证首屏的加载,我们需要从设计上考虑,在内容请求完成之前,可以优先保证 App Shell 的渲染,做到和 Native App 一样的体验,App Shell 是 PWA 界面展现所需的最小资源。

更多的资料可以参考: App Shell 设计规范

粘性

PWA具有的粘性表现在如下几个方面:

  • PWA 是可以安装的,用户点击安装到桌面后,会在桌面创建一个 PWA 应用,并且不需要从应用商店下载;
  • PWA 可以借助 Web App Manifest 提供给用户和 Native App 一样的沉浸式体验;
  • PWA 可以通过给用户发送离线通知,让用户回流。

同时,Web App Manifest 允许开发者控制 PWA 添加到桌面,允许定制桌面图标、URL等等。

关于Web App Manifest更多的内容可以参考:Web App ManifestPush Notification

其他

除此之外,讲到 PWA 兼具 Web App 和 Native App 的特征的,Web App 无版本问题、可索引也是很重要的特性。

总结一下,PWA 具有下面一些特性:

  • 渐进式 - 适用于所有浏览器,因为它是以渐进式增强作为宗旨开发的。
  • 连接无关性 - 能够借助 Service Worker 在离线或者网络较差的情况下正常访问。
  • 类似应用 - 由于是在 App Shell 模型基础上开发,因为应具有 Native App 的交互和导航,给用户 Native App的体验。
  • 持续更新 - 始终是最新的,无版本和更新问题。
  • 安全 - 通过 HTTPS 协议提供服务,防止窥探和确保内容不被篡改。
  • 可索引 - 应用清单文件和 Service Worker 可以让搜索引擎索引到,从而将其识别为『应用』。
  • 粘性 - 通过推送离线通知等,可以让用户回流。
  • 可安装 - 用户可以添加常用的 webapp 到桌面,免去去应用商店下载的麻烦。
  • 可链接 - 通过链接即可分享内容,无需下载安装。

PWA 是对站点体验的一个飞跃式的提升,可以在移动设备上的 Chrome(version > 52) 访问 天气PWA 体验一下。

渐进式

所谓渐进式,就是逐步的改善,不是一蹴而就的,采取这种方案,主要有两点原因:

  • 降低站点改造的代价,逐步支持各项新技术,不要一蹴而就;
  • 新技术标准的支持度还不完全,新技术的标准还未完全确定。

所以,从改造的成本考虑,我们也建议采取渐进式的方式,可以考虑按照下面的步骤来改造:

  • 第一步,应该是安全,将全站 HTTPS 化,因为这是 PWA 的基础,没有 HTTPS,就没有 Service Worker
  • 第二步,应该是 Service Worker 来提升基础性能,离线提供静态文件,把用户首屏体验提升上来
  • 第三步,App Manifest,这一步可以和第二步同时进行 后续,再考虑其他的特性,离线消息推送等

同时,PWA作为最新的不太成熟的技术,当前浏览器还没有达到完全支持的程度,W3C 关于这些技术的标准也还在处于草稿状态,没有定稿。根据知名统计网站Can I use 的统计,对PWA相关技术的支持程度如下:

  • App Manifest 的支持度达到 57.43%;
  • Service Worker 的支持度达到 72.82%;
  • Notifications API 的支持度达到 43.3%;
  • Push API 的支持度达到 72.39%;
  • Background Sync 暂未统计到,Chrome 49 以上均支持。

比较遗憾的是上面提到的所有技术,目前只有 Android 的部分浏览器支持,iOS 的Safari暂不支持,不过,Safari 浏览器已经在考虑了。

Service Worker

W3C 组织早在 2014 年 5 月就提出过 Service Worker 这样的一个 HTML5 API ,主要用来做持久的离线缓存。对于API相关的内容,这里仔细整理了一下:
浏览器中的 javaScript 都是运行在一个单一主线程上的,在同一时间内只能做一件事情。随着 Web 业务不断复杂,我们逐渐在 js 中加了很多耗资源、耗时间的复杂运算过程,这些过程导致的性能问题在 WebApp 的复杂化过程中更加凸显出来。

W3C 组织早早的洞察到了这些问题可能会造成的影响,这个时候有个叫 Web Worker 的 API 被造出来了,这个 API 的唯一目的就是解放主线程,Web Worker 是脱离在主线程之外的,将一些复杂的耗时的活交给它干,完成后通过 postMessage 方法告诉主线程,而主线程通过 onMessage 方法得到 Web Worker 的结果反馈。

一切问题好像是解决了,但 Web Worker 是临时的,我们能不能有一个东东是一直持久存在的,并且随时准备接受主线程的命令呢?基于这样的需求推出了最初版本的 Service Worker ,Service Worker 在 Web Worker 的基础上加上了持久离线缓存能力。当然在 Service Worker 之前也有在 HTML5 上做离线缓存的 API 叫 AppCache, 但是 AppCache 存在很多 不能忍受的缺点

W3C 决定 AppCache 仍然保留在 HTML 5.0 Recommendation 中,在 HTML 后续版本中移除。

WHATWG HTML5 作为 Live Standard,也将 AppCache 标注为 Discouraged 并引导至 Service Worker。Ok ,那么 Service Worker 到底用来干啥的呢?

Service Worker 有以下功能和特性:

  • 一个独立的 worker 线程,独立于当前网页进程,有自己独立的 worker context。
  • 一旦被 install,就永远存在,除非被 uninstall。
  • 需要的时候可以直接唤醒,不需要的时候自动睡眠(有效利用资源,此处有坑)。
  • 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)。
  • 离线内容开发者可控。
  • 能向客户端推送消息。
  • 不能直接操作 DOM。
  • 出于安全的考虑,必须在 HTTPS 环境下才能工作。
  • 异步实现,内部大都是通过 Promise 实现。

所以我们基本上知道了 Service Worker 的伟大使命,就是让缓存做到优雅和极致,让 Web App 相对于 Native App 的缺点更加弱化,也为开发者提供了对性能和体验的无限遐想。

浏览器Service Worker支持情况

根据Can I use 发现,目前市场上对Service Worker的支持情况如下:
这里写图片描述
从这张图可以发现,Chrome 作为开路先锋早早的在 V40 版本就支持了,还提供了完善的 debug 方案( Service Worker debug );Firefox,Opera 不甘示弱在后续版本也进行了支持;安卓手机 4.x 以上版本新系统形势一片大好(具体各手机的实现还得进一步探测);安卓 Chrome 同样给力;但是目前IE和Safair是不支持的,不过已经被列入未来的支持计划中。

Service Worker使用

Service Worker 出于安全性和其实现原理,在使用的时候有一定的前提条件。

  • 由于 Service Worker 要求 HTTPS 的环境,我们通常可以借助于 github
    page
    进行学习调试。当然一般浏览器允许调试 Service Worker
    的时候 host 为 localhost 或者 127.0.0.1 也是 ok 的。
  • Service Worker 的缓存机制是依赖 Cache API 实现的。
  • 依赖 HTML5 fetch API
  • 依赖 Promise 实现。

Service Worker注册

要安装 Service Worker, 我们需要通过在 js 主线程(常规的页面里的 js )注册 Service Worker 来启动安装,这个过程将会通知浏览器我们的 Service Worker 线程的 javaScript 文件在什么地方呆着。先看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/sw.js', {scope: '/'})
.then(function (registration) {
// 注册成功
console.log('ServiceWorker registration successful with scope: ', registration.scope);
})
.catch(function (err) {
// 注册失败:(
console.log('ServiceWorker registration failed: ', err);
});
});
}

这段代码首先判断 Service Worker API 的可用情况,支持的话咱们才继续谈实现。如果支持的话,在页面 onload 的时候注册位于 /sw.js 的 Service Worker。每次页面加载成功后,就会调用 register() 方法,浏览器将会判断 Service Worker 线程是否已注册并做出相应的处理。register 方法的 scope 参数是可选的,用于指定你想让 Service Worker 控制的内容的子目录。本 demo 中服务工作线程文件位于根网域, 这意味着服务工作线程的作用域将是整个来源。

说明: Service Worker 线程将接收 scope 指定网域目录上所有事项的 fetch 事件,如果我们的 Service Worker 的 javaScript 文件在 /a/b/sw.js, 不传 scope 值的情况下, scope 的值就是 /a/b。
scope 的值的意义在于,如果 scope 的值为 /a/b, 那么 Service Worker 线程只能捕获到 path 为 /a/b 开头的( /a/b/page1, /a/b/page2,…)页面的 fetch 事件。通过 scope 的意义我们也能看出 Service Worker 不是服务单个页面的,所以在 Service Worker 的 js 逻辑中全局变量需要慎用。

then() 函数链式调用我们的 promise,当 promise resolve 的时候,里面的代码就会执行。代码执行完成之后,我们这就注册了一个 Service Worker,它工作在 worker context,所以没有访问 DOM 的权限。在正常的页面之外运行 Service Worker 的代码来控制它们的加载。

为了验证Service Worker 到底有没有注册成功,可以在 PC 上打开chrome 浏览器,并输入:chrome://inspect/#service-workers。
这里写图片描述
我们还可以通过 chrome://serviceworker-internals 来查看服务工作线程详情。 如果只是想了解服务工作线程的生命周期,这仍很有用,但是日后其很有可能被 chrome://inspect/#service-workers 完全取代。

当然,它还可用于测试隐身窗口中的 Service Worker 线程,您可以关闭 Service Worker 线程并重新打开,因为之前的 Service Worker 线程不会影响新窗口。从隐身窗口创建的任何注册和缓存在该窗口关闭后均将被清除。

安装

在你的 Service Worker 注册成功之后呢,我们的浏览器中已经有了一个属于你自己 web App 的 worker context 啦, 在此时,浏览器就会马不停蹄的尝试为你的站点里面的页面安装并激活它,并且在这里可以把静态资源的缓存给办了。

install 事件我们会绑定在 Service Worker 文件中,在 Service Worker 安装成功后,install 事件被触发。

install 事件一般是被用来填充你的浏览器的离线缓存能力。为了达成这个目的,我们使用了 Service Worker 新的标志性的存储 cache API — 一个 Service Worker 上的全局对象,它使我们可以存储网络响应发来的资源,并且根据它们的请求来生成key。这个 API 和浏览器的标准的缓存工作原理很相似,但是是只对应你的站点的域的。它会一直持久存在,直到你告诉它不再存储,你拥有全部的控制权。

localStorage 的用法和 Service Worker cache 的用法很相似,但是由于 localStorage 是同步的用法,所以不允许在 Service Worker 中使用。 IndexedDB 也可以在 Service Worker 内做数据存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
this.addEventListener('install', function (event) {
event.waitUntil(
caches.open('my-test-cache-v1').then(function (cache) {
return cache.addAll([
'/',
'/index.html',
'/main.css',
'/main.js',
'/image.jpg'
]);
})
);
});

自动更新页面

如果希望在有了新版本时,所有的页面都得到及时自动更新怎么办呢?可以在 install 事件中执行 self.skipWaiting() 方法跳过 waiting 状态,然后会直接进入 activate 阶段。接着在 activate 事件发生时,通过执行 self.clients.claim() 方法,更新所有客户端上的 Service Worker。例如下面的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 安装阶段跳过等待,直接进入 active
self.addEventListener('install', function (event) {
event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', function (event) {
event.waitUntil(
Promise.all([
// 更新客户端
self.clients.claim(),
// 清理旧版本
caches.keys().then(function (cacheList) {
return Promise.all(
cacheList.map(function (cacheName) {
if (cacheName !== 'my-test-cache-v1') {
return caches.delete(cacheName);
}
})
);
})
])
);
});

另外要注意一点,/sw.js 文件可能会因为浏览器缓存问题,当文件有了变化时,浏览器里还是旧的文件。这会导致更新得不到响应。如遇到该问题,可尝试这么做:在 Web Server 上添加对该文件的过滤规则,不缓存或设置较短的有效期。

手动更新 Service Worker

其实在页面中,也可以手动借助 Registration.update() 更新。例如,下面的示例代码:

1
2
3
4
5
6
7
8
9
var version = '1.0.1';
navigator.serviceWorker.register('/sw.js').then(function (reg) {
if (localStorage.getItem('sw_version') !== version) {
reg.update().then(function () {
localStorage.setItem('sw_version', version)
});
}
});

同时,Service Worker debug 技巧 中也会提到, Service Worker 被载入后立即激活可以保证每次 /sw.js 为最新的。代码如下:

1
2
3
self.addEventListener('install', function () {
self.skipWaiting();
});

Service Worker 生命周期

Service Worker 的使用过程很简单,所处理的事情也相对单一,我们基本上需要做的就是利用这个 API 做好站点的缓存策略。在页面脚本中注册 Service Worker 文件所在的 URL。Worker 就可以开始激活了,激活后的 Service Worker 可以监听当前域下的功能性事件,比如资源请求(fetch)、推送通知(push)、后台同步(sync)。在这一系列的流程中,从 Service Worker 的注册到消失,经历了生命周期中不同的状态。

Service Worker 工作流程

Service Worker 基本就是以下几个步骤:

  1. 首先,我们需要在页面的JavaScript 主线程中使用 serviceWorkerContainer.register() 来注册Service Worker ,在注册的过程中,浏览器会在后台启动尝试 Service Worker 的安装步骤。
  2. 如果注册成功,Service Worker 在 ServiceWorkerGlobalScope 环境中运行; 这是一个特殊的 worker context,与主脚本的运行线程相独立,同时也没有访问 DOM 的能力。
  3. 后台开始安装步骤, 通常在安装的过程中需要缓存一些静态资源。如果所有的资源成功缓存则安装成功,如果有任何静态资源缓存失败则安装失败,在这里失败的不要紧,会自动继续安装直到安装成功,如果安装不成功无法进行下一步 激活 Service Worker操作。
  4. 开始激活 Service Worker,必须要在 Service Worker 安装成功之后,才能开始激活步骤,当 Service Worker 安装完成后,会接收到一个激活事件(activate event)。激活事件的处理函数中,主要操作是清理旧版本的 Service Worker 脚本中使用资源。
  5. 激活成功后 Service Worker 可以控制页面了,但是只针对在成功注册了 Service Worker 后打开的页面。也就是说,页面打开时有没有 Service Worker,决定了接下来页面的生命周期内受不受 Service Worker 控制。所以,只有当页面刷新后,之前不受 Service Worker 控制的页面才有可能被控制起来。

Service Worker生命周期

下面是MDN 给出了详细的 Service Worker 生命周期图:
这里写图片描述

由上图可知,Service Worker的生命周期主要分为以下几个阶段:安装中, 安装后, 激活中, 激活后, 废弃。

安装

这个状态发生在 Service Worker 注册之后,表示开始安装,触发 install 事件回调指定一些静态资源进行离线缓存。
install 事件回调中有两个方法:

  • event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
  • self.skipWaiting():self 是当前 context 的 global 变量,执行该方法表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态。

安装后( installed )

Service Worker 已经完成了安装,并且等待其他的 Service Worker 线程被关闭。

激活( activating )

在这个状态下没有被其他的 Service Worker 控制的客户端,允许当前的 worker 完成安装,并且清除了其他的 worker 以及关联缓存的旧缓存资源,等待新的 Service Worker 线程被激活。

激活的状态回调中有两个方法:

  • event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
  • self.clients.claim():在 activate 事件回调中执行该方法表示取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 Service Worker 脚本不再控制着页面,之后会被停止。

激活后( activated )

在这个状态会处理 activate 事件回调 (提供了更新缓存策略的机会)。并可以处理功能性的事件 fetch (请求)、sync (后台同步)、push (推送)。

废弃状态 ( redundant )

这个状态表示一个 Service Worker 的生命周期结束。废弃状态可能有以下几种:

  • 安装 (install) 失败;
  • 激活 (activating) 失败;
  • 新版本的 Service Worker 替换了它并成为激活状态。

Service Worker支持的事件

同时,MDN 也列出了 Service Worker 所有支持的事件,如下图所示:
这里写图片描述

  • install:Service Worker 安装成功后被触发的事件,在事件处理函数中可以添加需要缓存的文件。
  • activate:当 Service Worker 安装完成后并进入激活状态,会触发 activate 事件。通过监听 activate 事件你可以做一些预处理,如对旧版本的更新、对无用缓存的清理等。
  • message:Service Worker 运行于独立 context 中,无法直接访问当前页面主线程的 DOM 等信息,但是通过 postMessage API,可以实现他们之间的消息传递,这样主线程就可以接受 Service Worker 的指令操作 DOM。

Service Worker 有几个重要的功能性的的事件,这些功能性的事件支撑和实现了 Service Worker 的特性。

  • fetch (请求):当浏览器在当前指定的 scope 下发起请求时,会触发 fetch 事件,并得到传有 response参数的回调函数,回调中就可以做各种代理缓存的事情了。
  • push (推送):push 事件是为推送准备的。不过首先需要了解一下 Notification APIPUSH API。通过 PUSH API,当订阅了推送服务后,可以使用推送方式唤醒 Service Worker 以响应来自系统消息传递服务的消息,即使用户已经关闭了页面。
  • sync (后台同步):sync 事件由 background sync (后台同步)发出。background sync 配合 Service Worker 推出的 API,用于为 Service Worker 提供一个可以实现注册和监听同步处理的方法。但它还不在 W3C Web API 标准中。在 Chrome 中这也只是一个实验性功能,需要访问 chrome://flags/#enable-experimental-web-platform-features ,开启该功能,然后重启生效。

Service Worker 调试

Service Worker 作为独立于主线程的独立线程,在调试方面有其实和常规的 JavaScript 开发类似,我们关注的点大概有如下几点:

  • 代码是否有报错;
  • Service Worker 能否顺利更新;
  • 在不同机型上的兼容性问题 debug;
  • 不同类型资源和请求的缓存策略的验证。

debug 环境下等待状态

根据 Service Worker 生命周期的特性,如果浏览器还在使用旧的 Service Worker 版本,即使有 Service Worker 新的版本也不会立即被浏览器激活,只能进行安装并进入等待状态,直到浏览器 Tab 标签被重新关闭打开。

在开发调试 Service Worker 时肯定希望重新加载后立即激活,我们不希望每次都重新打开当前页面调试,为此我们可以在 install 事件发生时通过 skipWaiting() 来设置 skip waiting 标记。 这样每次 Service Worker 安装后就会被立即激活。

1
2
3
4
5
self.addEventListener('install', function () {
if (ENV === 'development') {
self.skipWaiting();
}
});

但是当浏览器未检测到 Service Worker 发生变化时(比如该文件设置了 HTTP 缓存), 甚至连安装都不会被触发。现在可以借助于浏览器 DevTools 调试了: 比如在 Chrome DevTools 的 Application 标签页勾选 Update on reload,Chrome 会在每次刷新时去访问 Service Worker 文件并重新安装和激活。

借助 Chrome 浏览器进行 debug

使用 Chrome 浏览器,可以通过进入控制台 Application -> Service Workers 面板查看和调试。如下图所示:
这里写图片描述
如果 Service Worker 线程已安装到当前打开的页面上,您会看到它将列示在此窗格中。 例如,在上方的屏幕截图中,https://lavas-project.github.io/lavas-demo/news-v2/#/ 的作用域内安装了一个 Service Worker 线程。

其中,Service Worker会有以下几个选项:

  • offline: 复选框可以将 DevTools 切换至离线模式。它等同于 Network 窗格中的离线模式。
  • Update on reload:复选框可以强制 Service Worker 线程在每次页面加载时更新。
  • Bypass for network:复选框可以绕过 Service Worker 线程并强制浏览器转至网络寻找请求的资源。
  • Update:按钮可以对指定的 Service Worker 线程执行一次性更新。
  • Push:按钮可以在没有负载的情况下模拟推送通知。
  • Sync:按钮可以模拟后台同步事件。
  • Unregister:按钮可以注销指定的 Service Worker 线程。
  • Source:告诉您当前正在运行的 Service Worker 线程的安装时间。 链接是 Service Worker线程源文件的名称。点击链接会将您定向至 Service Worker 线程来源。
  • Status:告诉您 Service Worker 线程的状态。此行上的数字(上方屏幕截图中的 #1)指示 Service Worker线程已被更新的次数。如果启用 update on reload 复选框,您会注意到每次页面加载时此数字都会增大。在状态旁边,您将看到 start 按钮(如果 Service Worker 线程已停止)或 stop 按钮(如果 Service Worker 线程正在运行)。 Service Worker 线程设计为可由浏览器随时停止和启动。 使用 stop 按钮明确停止 Service Worker 线程可以模拟这一点。停止 Service Worker 线程是测试 Service Worker线程再次重新启动时的代码行为方式的绝佳方法。它通常可以揭示由于对持续全局状态的不完善假设而引发的错误。
  • Clients:告诉您 Service Worker 线程作用域的原点。 如果您已启用 show all 复选框,focus按钮将非常实用。 在此复选框启用时,系统会列出所有注册的 Service Worker 线程。 如果您点击正在不同标签中运行的Service Worker 线程旁的 focus 按钮,Chrome 会聚焦到该标签。

如果 Service Worker 文件在运行过程中出现了任何的错误,将显示一个 Error 新标签。例如:
这里写图片描述

当然我们也可以直接访问 Chrome://serviceworker-internals 来打开 serviceWorker 的配置面板,查看所有注册的 Service Worker 情况。注意一点,如无必要,不要选中顶部的 Open DevTools window and pause javaScript execution on Service Worker startup for debugging 复选框,否则每当刷新页面调试时都会弹出一个开发者窗口来。

在 Firefox 中,可以通过 Tools -> Web Developer -> Service Workers 打开调试面板。也可以访问 about:debugging#workers 直接进入该面板。

Service Worker 缓存内容

我们知道,Service Worker 使用 Cache API 缓存只读资源,我们同样可以在 Chrome DevTools 上查看缓存的资源列表。Cache Storage 选项卡提供了一个已使用(Service Worker 线程)Cache API 缓存的只读资源列表。
这里写图片描述

这里有个地方需要注意一下:第一次打开缓存并向其添加资源时,Chrome DevTools 可能检测不到更改。 重新加载页面后,您应当可以看到缓存。
这里写图片描述
当然,Cache Storage 提供清除 Cache 列表的功能,在选择 Cache Storage 选项卡后在 Cache Storge 缓存的 key 的 item 上右键点击出现 delete ,点击 delete 就可以清除该缓存了。
这里写图片描述

网络跟踪调试

此外经过 Service Worker 的 fetch 请求 Chrome 都会在 Chrome DevTools Network 标签页里标注出来,其中:

  • 来自 Service Worker 的内容会在 Size 字段中标注为 from ServiceWorker。
  • Service Worker 发出的请求会在 Name 字段中添加 ⚙ 图标。

例如下图中,第一个名为 300 的请求是一张 jpeg 图片, 其 URL 为 https://unsplash.it/200/300,该请求是由 Service Worker 代理的, 因此被标注为 from ServiceWorker。

为了响应页面请求,Service Worker 也发出了名为 300 的请求(这是图中第二个请求), 但 Service Worker 把 URL 改成了 https://unsplash.it/g/200/300,因此返回给页面的图片是灰色的。

这里写图片描述

实例

所谓“工欲善其事,必先利其器”,在开始开发PWA程序之前,需要安装好相关的运行环境,PWA需要Node 和 Ngrok 支持。关于Node 的使用以及安装我就不说啦,而Ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。ngrok 可捕获和分析所有通道上的流量,便于后期分析和重放。

Ngrok安装使用

读者可以到Ngrok官网下载它,解压后某个目录。

需要注意的是,有的人说需要注册ngrok得到your auth token,后面的自定义域名必须要有这个token,因为我暂时不需要自定义域名,所以就省略了这一步。
然后在解压后的Ngrok目录下使用命令:./ngrok help
这里写图片描述

然后,使用如下命令来开启服务:

1
./ngrok http localhost:9988

这里写图片描述

其中,http://dae1a9d6.ngrok.io和https://dae1a9d6.ngrok.io就是本地localhost映射的外网地址,注意这个映射外网地址是变化的,也就是你每次启动ngrok,获取的外网地址是不一样的,貌似如果想使用固定外网地址,就需要收费。

然后输入http://dae1a9d6.ngrok.io地址就可以访问了。

支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者

上一篇