1.Service worker是什么?
service worker的出现,是为了能够使web app拥有与native app或者本地应用程序相同的离线使用、消息推送、后台自动更新的能力。
service worker是一段脚本,浏览器独立于当前网页,将其在后台运行,它推出的第一个首要特性,就是拦截和处理网络请求的能力,包括以编程方式来管理被缓存的响应。
service worker可以:
1.后台消息传递
2.网络代理,转发请求,伪造响应
3.离线缓存
4.消息推送
关于 service worker 的注意:
service worker 是一个JavaScript worker (可以先去了解什么是web worker http://www.w3school.com.cn/html5/html_5_webworkers.asp),所以它不能直接访问 DOM 。相反, service worker 可以通过postMessage 接口与跟其相关的页面进行通信,发送消息,从而让这些页面在有需要的时候去操纵 DOM 。
Service worker 是一个可编程的网络代理,允许你去控制如何处理页面的网络请求。
Service worker 在不使用时将被终止,并会在需要的时候重新启动,因此你不能把onfetch 和 onmessage事件来作为全局依赖处理程序。如果你需要持久话一些信息并在重新启动Service worker后使用他,可以使用 IndexedDB API,service worker 支持。
-
在开发过程中,可以通过 localhost 使用服务工作线程,但如果要在网站上部署服务工作线程,需要在服务器上设置 HTTPS。
使用服务工作线程,您可以劫持连接、编撰以及过滤响应。 这是一个很强大的工具。您可能会善意地使用这些功能,但中间人可会将其用于不良目的。 为避免这种情况,可仅在通过 HTTPS 提供的页面上注册服务工作线程,如此我们便知道浏览器接收的服务工作线程在整个网络传输过程中都没有被篡改。
2.Service worker的生命周期
通常遵循以下基本步骤来使用 service workers:
-
- service worker URL 通过
serviceWorkerContainer.register()
接口的 register() 方法创建或更新一个给定scriptURL的ServiceWorkerRegistration 来获取和注册。
- service worker URL 通过
-
- 如果注册成功,service worker 就在
ServiceWorkerGlobalScope
环境中运行; 这是一个特殊类型的 woker 上下文运行环境,与主运行线程(执行脚本)相独立,同时也没有访问 DOM 的能力。
- 如果注册成功,service worker 就在
-
- service worker 现在可以处理事件了。
-
- 受 service worker 控制的页面打开后会尝试去安装 service worker。最先发送给 service worker 的事件是安装事件(在这个事件里可以开始进行填充 IndexDB和缓存站点资源)。这个流程同原生 APP 或者 Firefox OS APP 是一样的 — 让所有资源可离线访问。
-
- 当
oninstall
事件的处理程序执行完毕后,可以认为 service worker 安装完成了。
- 当
-
- 下一步是激活。当 service worker 安装完成后,会接收到一个激活事件(activate event)。
onactivate
主要用途是清理先前版本的service worker 脚本中使用的资源。
- 下一步是激活。当 service worker 安装完成后,会接收到一个激活事件(activate event)。
-
- Service Worker 现在可以控制页面了,但仅是在
register()
成功后的打开的页面。也就是说,页面起始于有没有 service worker ,且在页面的接下来生命周期内维持这个状态。所以,页面不得不重新加载以让 service worker 获得完全的控制。
- Service Worker 现在可以控制页面了,但仅是在
1)注册
要安装service worker,您需要通过在页面中对其进行注册来启动安装。 这将告诉浏览器service worker 的JavaScript 文件的位置。
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/htmlcache.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
}
这里/htmlcache.js这个文件将会作为service worker被注册,如果这个 service worker 已经被注册过,浏览器会自动忽略上面的代码。
Service worker的作用域
一个 service worker 的默认作用域是这个 service worker 脚本所在的目录。
在上面例子中,service worker 文件被放在这个域的根目录下,这个 service worker 将会获取到这个域下的所有 fetch 事件。如果 service worker文件注册到/example/sw.js ,那么 service worker 只能收到 /example/ 路径下的 fetch 事件(比如: /example/page1/, /example/page2/)。
你也可以在注册 service worker 时明确指定作用域:
navigator.serviceWorker.register("/htmlcache?bid=<%= bid %>", { scope: "/abc" })
.then(function() {
window._timePoints[29] = +new Date(); // 注册service worker 完成
})
.catch(function(err) {
console.error("[Service worker] Install fail:" + err);
});
假设以上代码在 https://example.com 页面里执行,则意味着该 service worker 的作用域就是 https://example.com/abc 下的页面。
2) installing安装中
在你所控制的页面上完成 service worker 的注册步骤之后,让我们把注意力转到 service worker 的脚本中,也就是register的第一个参数,我们要在其中完成它的安装。
在浏览器首次加载Service Worker或者Service Worker发生了更改后,Service Worker就会进入installing状态,并且触发install事件。这个时候,我们就可以为Web应用的启动进行一些初始化的工作,例如下载并缓存必要的资源文件,或者往indexedDB中填充数据,就和平时下载安装本地App时所做的工作一致。一旦完成准备工作,我们就可以通过调用event.waitUntil方法告知浏览器Service Worker已经安装完成了。
self.addEventListener("install", (event) => {
console.log("Service Worker正在安装中");
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'
]);
})
);
});
在
waitUntil()
内,我们使用了caches.open()
方法来创建了一个叫做v1
的新的缓存,将会是我们的站点资源缓存的第一个版本。它返回了一个创建缓存的 promise,当它 resolved的时候,我们接着会调用在创建的缓存示例上的一个方法addAll()
,这个方法的参数是一个由一组相对于 origin 的 URL 组成的数组,这些 URL 就是你想缓存的资源的列表。如果 promise 被 rejected,安装就会失败,这个 worker 不会做任何事情。这也是可以的,因为你可以修复你的代码,在下次注册发生的时候,又可以进行尝试。
注意: localStorage 跟 service worker 的 cache 工作原理很类似,但是它是同步的,所以不允许在 service workers 内使用。
注意: IndexedDB 可以在 service worker 内做数据存储。
3)installed(已安装)
在安装完成后,Service Worker就会进入installed状态。这时如果浏览器中还有其他页面运行着该Service Worker的旧版本,那么新的Service Worker就会一直处于等待的状态。
这里要注意,浏览器校验Service Worker是否发生更改是在页面完全加载以后,这时候如果Service Worker发生了更改,即使当前只打开了本页面,但由于旧的Service Worker已经在本页面上启动了,新的Service Worker还是需要等待下一次页面刷新的时候才会进入activating状态。
4) activating(激活中)
经过了installed状态,Service Worker就会进入activating状态,并且触发activate事件。这个时候,我们就可以为Web应用的完成一些清理过时资源的工作,例如删掉没用的资源文件,或者清空indexedDB中没用的数据。一旦完成清理的工作,我们就可以调用event.waitUntil方法告知浏览器Service Worker已经激活完成了。
self.addEventListener('activate', function (event) { // 监听worker的activate事件
event.waitUntil(
//更新缓存
})
)
});
5)fetch截取页面的资源请求
现在你已经将你的站点资源缓存了,你需要告诉 service worker 让它用这些缓存内容来做点什么。有了 fetch 事件,这是很容易做到的。
每次任何被 service worker 控制的资源被请求到时,都会触发 fetch 事件,这些资源包括了指定的 scope 内的文档,和这些文档内引用的其他任何资源(比如 index.html 发起了一个跨域的请求来嵌入一个图片,这个也会通过 service worker 。)
你可以给 service worker 添加一个 fetch 的事件监听器,接着调用 event 上的 respondWith() 方法来劫持我们的 HTTP 响应,然后你用可以用自己的魔法来更新他们。
this.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
);
});
caches.match(event.request) 允许我们对网络请求的资源和 cache 里可获取的资源进行匹配,查看是否缓存中有相应的资源。这个匹配通过 url 和 vary header进行,就像正常的 http 请求一样。