1.介绍
PWA应用是指那些使用指定技术和标准模式来开发的web应用,这将同时赋予它们web应用和原生应用的特性。例如:
Discoverable, 内容可以通过搜索引擎发现。
Installable, 可以出现在设备的主屏幕。
Linkable, 你可以简单地通过一个URL来分享它。
Network independent, 它可以在离线状态或者是在网速很差的情况下运行。
Progressive, 它在老版本的浏览器仍旧可以使用,在新版本的浏览器上可以使用全部功能。
Re-engageable, 无论何时有新的内容它都可以发送通知。
Responsive, 它在任何具有屏幕和浏览器的设备上可以正常使用——包括手机,平板电脑,笔记本,电视,冰箱,等。
Safe, 在你和应用之间的连接是安全的,可以阻止第三方访问你的敏感数据。
2. demo
service worker能劫持所有的网络请求,安全原因必须要在https或者localhost下运行。以下是一个项目地址为 https://mdn.github.io/sw-test 的例子,步骤如下:
2.1 注册
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' })
.then(function(reg) {
// registration worked
console.log('Registration succeeded. Scope is ' + reg.scope);
})
.catch(function(error) {
// registration failed
console.log('Registration failed with ' + error);
});
}
该段代码检测是否支持serviceWorker,支持的话注册/sw-test/sw.js(url 是相对于域名),scope选填,表示service worker影响范围(该范围不能比注册的地方大,在该例子中,最大就是/sw-test/。除非后端设置了Service-Worker-Allowed),默认是当前路径(https://mdn.github.io/sw-test)
2.2 安装
Sw.js中完成具体逻辑,如下:
this.addEventListener('install', function(event) {
event.waitUntil(
caches.open('v1').then(function(cache) {
return cache.addAll([
'/sw-test/',
'/sw-test/index.html',
'/sw-test/style.css',
'/sw-test/app.js',
'/sw-test/image-list.js',
'/sw-test/star-wars-logo.jpg',
'/sw-test/gallery/',
'/sw-test/gallery/bountyHunters.jpg',
'/sw-test/gallery/myLittleVader.jpg',
'/sw-test/gallery/snowTroopers.jpg'
]);
})
);
});
- Event.waitUntil确保Service Worker 不会在
waitUntil()
里面的代码执行完毕之前安装完成。 caches.open()
方法来创建了一个叫做v1
的新的缓存addAll()
这个方法的参数是一个由一组相对于 origin 的 URL 组成的数组,这些 URL 就是你想缓存的资源的列表,他会马上去加载一次这些资源,并且缓存。
当promise被reject的时候,安装失败,反之安装成功,service worker就会激活。
2.3 劫持
每次任何被 service worker 控制的资源被请求到时,都会触发 fetch
事件,这些资源包括了指定的 scope 内的文档,和这些文档内引用的其他任何资源(比如 index.html
发起了一个跨域的请求来嵌入一个图片,这个也会通过 service worker 。)
this.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
);
});
以上例子只是简单的响应这些缓存中的 url 和网络请求匹配的资源。
3. 生命周期
Installing
安装sw,执行install钩子。可以进行一些资源缓存,数据初始化(indexedDB)
installed
已经安装完成,等待使用了旧版本的sw的页面关闭
activating
激活sw,执行active钩子
activate
激活成功之后才能开始管控页面,fetch这些钩子才开始有效
redundant
失败获取被其他sw取代
4. 更新顺序
页面上通过navigator.serviceWorker.register来获取和注册。
受 service worker 控制的页面打开后会尝试去安装 service worker。最先发送给 service worker 的事件是安装事件(在这个事件里可以开始进行填充 IndexDB和缓存站点资源)。这个流程同原生 APP 或者 Firefox OS APP 是一样的 — 让所有资源可离线访问。
当
oninstall
事件的处理程序执行完毕后,可以认为 service worker 安装完成了。下一步是激活。当 service worker 安装完成后,会接收到一个激活事件(activate event)。
onactivate
主要用途是清理先前版本的service worker 脚本中使用的资源。重新刷新页面,会检测sw.js有无变化。没变化,不会重新在浏览器注册一个新的service worker(install钩子不会执行),还是用旧的;有变化,会重新注册一个sw。期间,install钩子会直接执行,但是activate和其他不会执行,所以一般在activate清空上个版本的service worker相关缓存,等待使用旧的sevice worker的页面都关闭后,再激活新的service worker(如果使用了skipWaiting函数即可直接跳过install阶段)。未激活的service worker谷歌显示如下:
下次再打开页面的时候,同时触发install和activate钩子
Service Worker 现在可以控制页面了,但仅是在
register()
成功后的打开的页面。也就是说,页面起始于有或则没有 service worker ,且在页面的接下来生命周期内维持这个状态。所以,页面不得不重新加载以让 service worker 获得完全的控制。(在activate钩子里使用clients.claim可以立即接管页面)
5. 注意事项
5.1 全局变量
service worker运行在浏览器独立的线程,因此没有获取dom的权限。单个 service worker 可以控制很多页面。每个你的 scope 里的页面加载完的时候,安装在页面的 service worker 可以控制它。牢记你需要小心 service worker 脚本里的全局变量: 每个页面不会有自己独有的worker。
5.2 部分对象不可用
如window、localStorage、XMLHttpRequest在sw的上下文里是undefined,不确定的api用之前先测试一下。
5.3 域名
只能在https和localhost里面使用service worker,本地测试的时候请使用localhost(127.0.0.1实测也行),用其他ip都不行,’serviceWorker’ in navigator会返回false
5.4 不要更改service worker文件名
启用离线访问的情况下,首页(index.html)会被sw(service worker)缓存。如果sw文件名称修改了,因为首页被缓存了,注册的sw文件路径还是旧的,所以根本不会启用新的sw(找不到sw文件并不会删除或更新sw)。
5.5 调试
由于service worker独立存在,所以错误的service worker可能会导致页面不可访问等其他情况。可以先去application选项卡把service worker清除
6.各api兼容性
和service worker配套使用的api支持情况和service worker本身不一致,支持情况如下
6.1 serviceWorker
6.2 caches
caches对象mdn介绍。有match、 has、open、delete和keys四个函数,都是返回promise。兼容如下图
6.3 fetch
6.4 结论
直接service worker的就一定支持cache和fetch(2,3项功能不行),所以用如下代码即可:
if ('serviceWorker' in navigator) {
console.log('支持service worker')
navigator.serviceWorker.register('<%= process.env.VUE_APP_PWA_PATH %>service-worker.js')
.then(function(reg) {
// registration worked
console.log('Registration succeeded. Scope is ' + reg.scope);
})
.catch(function(error) {
// registration failed
console.log('Registration failed with ' + error);
});
} else {
console.log('不支持service worker')
}
7.手机实测
安卓app(x5): 支持navigator.serviceWorker属性, 但是install钩子不执行,原因未明
安卓app:
安卓自带浏览器: 正常。由于连手机无法正常查看浏览器相关cache。接口时间对比如下(http和https):
Ios safari:
iOS app:不支持。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!