Service Worker基础知识整理
Service Worker是什么
service worker 是独立于当前页面的一段运行在浏览器后端进程里的脚本。它的特性将包括推送消息,背景后端同步, geofencing(地理围栏定位),阻拦和解决网络请求。
这个 API 会让人兴奋的起因是,它可以使你的应用先访问本地缓存资源,所以在离线状态时,在没有通过网络接收到更多的数据前,仍可以提供基本的功能(一般称之为 Offline First)。
在 service worker 之前,另一个叫做 APP Cache 的 api 也可以提供离线体验。APP Cache 的的主要问题是坑比较多,而且其被设计为只适合于单页 web 应用程序,对于传统的多页网站则不适合。service worker 的设计规避了这些痛点。
关于 service worker 的少量注意点:
- service worker 是一个JavaScript worker ,所以它不能直接访问 DOM 。但 service worker 可以通过postMessage 接口与跟其相关的页面进行通信,发送消息,从而让这些页面在有需要的时候去操纵 DOM 。
- Service worker 是一个可编程的网络代理商,允许你去控制如何解决页面的网络请求, 可以解决fetch请求。
- Service worker 在不使用时将被终止,并会在需要的时候重新启动,因而你不能把onfetch 和 onmessage事件来作为全局依赖解决程序。假如你需要持久话少量信息并在重新启动Service worker后使用他,可以使用 IndexedDBAPI ,service worker 支持。
- Service Worker 的缓存机制是依赖 Cache API 实现的
- Service worker 广泛使用了 promise。
- Service worker依赖 HTML5 fetch API
- Service Workers 要求必需在 HTTPS 下才能运行
Service Worker生命周期
注册service worker,在网页上生效
安装成功,激活 或者者 安装失败(下次加载会尝试重新安装)
激活后,在sw的作用域下作用所有的页面,初次控制sw不会生效,下次加载页面才会生效。
sw作用页面后,解决fetch(网络请求)和message(页面消息)事件 或者者 被终止(节省内存)。
需要提前掌握的API
- Cache API基本使用
(1)检测api能否存在
if('caches' in window) { // Has support! }(2)caches.open,创立缓存总对象。如下创立名为 test-cache 的缓存。
caches.open('test-cache').then(function(cache) { // Cache is created and accessible });(3)cache.add和cache.addAll,增加缓存内容。其中cache.add只增加一个,cache.addAll可以增加多个。
caches.open('test-cache').then(function(cache) { cache.addAll(['/', '/images/logo.png']) .then(function() { // Cached! // or use cache.add cache.add('/page/1'); // "/page/1" URL will be fetched and cached! }); });(4)cache.keys(),查看已经缓存的数据
caches.open('test-cache').then(function(cache) { cache.keys().then(function(cachedRequests) { console.log(cachedRequests); // [Request, Request] }); });(5)cache.match和cache.matchAll,匹配缓存文件路径
caches.open('test-cache').then(function(cache) { cache.match('/page/1').then(function(matchedResponse) { console.log(matchedResponse); }); });(6)cache.delete,删除缓存。
caches.open('test-cache').then(function(cache) { cache.delete('/page/1'); });- Fetch API基本使用
(1)简单使用
// url (required), options (optional)fetch('https://davidwalsh.name/some/url', { method: 'get'}).then(function(response) { }).catch(function(err) { // Error :(});其中options对象包含以下属性:
- method – GET, POST, PUT, DELETE, HEAD
- url – 请求的链接
- headers – 请求的header对象
- referrer – 请求的referrer对象
- mode – cors, no-cors, same-origin
- credentials – 设置请求可不可以携带cookie
- redirect – follow, error, manual
- integrity – 子资源完整值
- cache – 缓存模式 (default, reload, no-cache)
(2)可以在fetch中传入Request对象实例:
var request = new Request('https://davidwalsh.name/users.json', { method: 'POST', mode: 'cors', redirect: 'follow', headers: new Headers({ 'Content-Type': 'text/plain' })});// Now use it!fetch(request).then(function() { /* handle response */ });(3)可以自己设置返回的Response对象实例,其中的options有:
- type – basic, cors
- url
- useFinalURL – 上面的url参数是不是最终的URL
- status – 状态码(ex: 200, 404, etc.)
- ok – 能否成功响应 (范围在 200-299)
- statusText – 状态码 (ex: OK)
- headers – 响应的headers对象
另外Response的实例还具有以下方法:
- clone() – 创立Response对象的克隆。
- error() – 返回与网络错误关联的新Response对象。
- redirect() – 使用不同的URL创立新响应。
- arrayBuffer() – 返回使用ArrayBuffer解析的promise。
- blob() – 返回使用Blob解析的promise。
- formData() – 返回使用FormData对象解析的promise。
- json() – 返回使用JSON对象解析的promise。
- text() – 返回使用USVString(文本)解析的promise。
// Create your own response for service worker testing// new Response(BODY, OPTIONS)var response = new Response('.....', { ok: false, status: 404, url: '/'});// The fetch's `then` gets a Response instance backfetch('https://davidwalsh.name/') .then(function(responseObj) { console.log('status: ', responseObj.status); });Service Worker的使用
- 兼容低版本,注入Cache API的一个polyfill,Service Worker需要依赖Cache API:
self.importScripts('./serviceworker-cache-polyfill.js');- 注册service worker:
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.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); });}上面的代码检查 service worker API 能否可用,假如可用, /sw.js 这个文件将会作为 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:
var CACHE_NAME = 'my-site-cache-v1';var urlsToCache = [ '/', '/styles/main.css', '/script/main.js'];self.addEventListener('install', function(event) { // Perform install steps event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); }) );});上面代码公告了需要缓存的内容,假如所有的文件都缓存成功,service worker 就安装成功了。假如任何一个文件下载失败,那么安装步骤就会失败。这个方式依赖于你自己指定的资源,但这意味着,你需要非常仔细地确定哪些文件需要被缓存。指定了太多文件的话,会添加失败率。
- 缓存和返回请求
self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return response if (response) { return response; } // IMPORTANT: Clone the request. A request is a stream and // can only be consumed once. Since we are consuming this // once by cache and once by the browser for fetch, we need // to clone the response var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic') { return response; } // IMPORTANT: Clone the response. A response is a stream // and because we want the browser to consume the response // as well as the cache consuming the response, we need // to clone it so we have 2 stream. var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }));假如我们想在缓存中增加新的请求缓存,可以通过解决fetch请求的response,将其增加到缓存中就可。代码里我们做了以下事情:
增加一个 callback 到 fetch 请求的 .then 方法中。
一旦我们取得一个 response,我们进行如下的检查:- 确保 response 有效
- 检查 response 的状态是200
- 确保 response 的类型是 basic 类型的,这说明请求是同源的,这意味着第三方的请求不能被缓存。
假如检查通过会clone 这个请求。这么做的起因是假如 response 是一个 Stream,那么它的 body 只能被消费一次。所以为了让浏览器跟缓存都使用这个body,我们必需克隆这个 body,一份到浏览器,一份到缓存中缓存。
- 重新激活
你的 service worker 总会有要升级的时候。在那时,你需要按照以下步骤来升级:
升级你 service worker 的 JavaScript 文件
当客户浏览你的网站时,浏览器尝试在后端重新下载 service worker 的脚本文件。经过比照,只需服务器上的文件和本地文件有一个字节不同,这个文件就认为是新的。之后升级后的 service worker 启动并触发 install 事件。
此时,当前页面生效的仍然是老版本的 service worker,新的 service worker 会进入 “waiting” 状态。
当页面关闭之后,老的 service worker 会被干掉,新的 servicer worker 接管页面
一旦新的 service worker 生效后会触发 activate 事件。
通常来讲,需要在 activate 的 callback 中进行 cache 管理,来清除老的 cache。我们在 activate 而不是 install 的时候进行的起因,是假如我们在 install 的时候进行清除,那么老的 service worker 依然在控制页面,他们依赖的缓存就失效了,因而就会忽然被中止。
之前我们使用的缓存可以叫 my-site-cache-v1 ,我们想把这个拆封到多个缓存,一份给页面使用,一份给博客文章使用。这意味着,install 步骤里,我们要创立两个缓存: pages-cache-v1 和 blog-posts-cache-v1。在 activite 步骤里,我们需要删除旧的 my-site-cache-v1。
下面的代码会遍历所有的缓存,并删除掉不在 cacheWhitelist 数组(我们定义的缓存白名单)中的缓存。
self.addEventListener('activate', function(event) { var cacheWhitelist = ['pages-cache-v1', 'blog-posts-cache-v1']; event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) );});具体实践
参考文章
http://kailian.github.io/2017/03/01/service-worker
https://davidwalsh.name/cache
https://juejin.im/post/5ba0fe356fb9a05d2c43a25c
https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers
https://lavas.baidu.com/doc/offline-and-cache-loading/service-worker/how-to-use-service-worker
https://davidwalsh.name/cache
https://zhuanlan.zhihu.com/p/20040372
https://zhuanlan.zhihu.com/p/28161855
https://www.villainhr.com/page/2017/01/08/Service%20Worker%20%E5%85%A8%E9%9D%A2%E8%BF%9B%E9%98%B6
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Service Worker基础知识整理