Service Worker 基本用法

花伊浓

看了很多介绍Service Worker的,看得都挺模糊的,所以决定自己写一篇文件整理一下思路。

一、Service Worker API 名词区分

1、ServiceWorkerContainer:navigator.serviceWorker返回的就是Service WorkerContainer对象,主要是用户在页面注册serviceWorker,调用方法:

navigator.serviceWorker.register(scriptURL, options)
    .then(function(ServiceWorkerRegistration) { ... })

2、ServiceWokrerGlobalScope:主要是用户在sw.js文件的全局变量,即this的指向
3、ServiceWorkerRegistration:在页面调用serviceWorker.register注册返回一个Promise对象,当resolve时传递给then的函数参数就是ServiceWorkerRegistration.
4、ServiceWorker:表示ServiceWorkerRegistration.installing || ServiceWorkerRegistration.waiting || ServiceWorkerRegistration.active

二、Service Worker 注意事项

1、建立在HTTPS上;
2、不支持缓存POST请求;

三、Service Worker 注册

1、index.html

<script>
    // register
    if("serviceWorker" in navigator){
        navigator.serviceWorker.register('./sw.js')
            .then(function(registration){
                console.log("Register success: ",registration.scope);
            })
            .catch(function(err){
                console.log("Register failed: ",err);
            });
    }else{
        console.log('Service workers are not supported.');
    }
</script>

2、sw.js

var CACHE_NAME = 'sw-test-v1';
this.addEventListener('install',function(event){
    console.log("installing...");
    event.waitUntil(caches.open(CACHE_NAME).then(function(cache){
        cache.addAll([
            'images/resource01.jpg',
            'images/resource02.jpg',
            ....
        ]);
    }));
});
this.addEventListener('fetch', function(event) {
    event.respondWith(
        caches.match(event.request)
            .then(function(response) {
                if (response) { // 缓存命中,返回缓存
                    return response;
                }
                // 请求是stream数据,只能使用一次,所以需要拷贝,一次用于缓存,一次用于浏览器请求
                var fetchRequest = event.request.clone();
                return fetch(fetchRequest)
                    .then(function(response) {
                        if(!response || response.status !== 200) {
                            return response;
                        }
                        // 响应也是stream,只能使用一次,一次用于缓存,一次用于浏览器响应
                        var responseToCache = response.clone();
                        caches.open(CACHE_NAME)
                            .then(function(cache) {
                                cache.put(event.request, responseToCache);
                            });
                        return response;
                    });
            })
  );
});

sw.js工作内容:首先监听install事件,调用cache.addAll()方法将静态资源加入缓存中。然后监听fetch事件,判断当前请求的url是否在缓存中,如果在则返回内容,如果不在,则向服务端发起请求数据,将返回的数据放入缓存中并且返回给浏览器。
代码中的方法解析:
1、caches.match():检查给定的Request对象或url字符串是否是一个已存储的 Response对象的key. 该方法针对 Response 返回一个 Promise ,如果没有匹配则返回 undefined 。cache对象按创建顺序查询,等同于在每个缓存上调用 cache.match() 方法 (按照caches.keys()返回的顺序) 直到返回Response 对象。语法如下:

caches.match(request, options).then(function(response) {
  // Do something with the response
});

参数解析:

options: 可选,配置对象中的属性控制在匹配操作中如何进行匹配选择,具体属性如下:

  • ignoreSearch: Boolean值, 指定匹配过程是否应该忽略url中查询参数,默认 false。
  • ignoreMethod: Boolean 值,当被设置为 true 时,将会阻止在匹配操作中对 Request请求的 http 方式的验证 (通常只允许 GET 和 HEAD 两种请求方式)。该参数默认为 false.
  • ignoreVary: Boolean 值,当该字段被设置为 true, 告知在匹配操作中忽略对VARY头信息的匹配。换句话说,当请求 URL 匹配上,你将获取匹配的 Response 对象,无论请求的 VARY 头存在或者没有。该参数默认为 false.
  • cacheName: DOMString 值, 表示所要搜索的缓存名称。

2、caches.open():返回一个resolve为匹配 cacheName 的 cache 对象的 Promise .如果指定的 cache 不存在,则使用该 cacheName 创建一个新的cache并返回。

caches.open(cacheName).then(function(cache) {});

3、cache.addAll():将静态资源加入缓存中

cache.addAll(requests[]).then(function() {
  // 已加入缓存
});

该方法会覆盖掉以前存储在缓存中的匹配的健值对,但是后面监听对fetch事件中调用cache.put()方法又会覆盖掉之前在cache.addAll()中添加到缓存中所匹配的健值对。
4、cache.put():允许将键/值对添加到当前的 Cache 对象中.它将覆盖先前存储在匹配请求的cache中的任何键/值对。
注意: Cache.add/Cache.addAll 不会缓存 Response.status 值不在200范围内的响应,而 Cache.put 允许你存储任何请求/响应对。因此,Cache.add/Cache.addAll 不能用于不透明的响应,而 Cache.put 可以。

cache.put(request, response).then(function() {
  // request/response pair has been added to the cache
});

5、event.waitUntil():扩展了事件的生命周期。在服务工作线程中,延长事件的寿命从而阻止浏览器在事件中的异步操作完成之前终止服务工作线程。
install事件中,它延迟将被安装的worker视为 installing ,直到传递的 Promise 被成功地resolve。主要用于确保:服务工作线程在所有依赖的核心cache被缓存之前都不会被安装。
activate事件中,它延迟将 active worker视为已激活的,直到传递的 Promise 被成功地resolve。这主要用于确保:任何功能事件不会被分派到 ServiceWorkerGlobalScope 对象,直到它升级数据库模式并删除过期的缓存条目。
当该方法运行时,如果 Promise 是resolved,任何事情都不会发生;如果 Promise 是rejected,installing 或者 active worker的 state 会被设置为redundant。
语法:event.waitUntil(promise)
6、event.respondWith():阻止浏览器默认的fetch处理方法,允许用户自己提供一个promise对象作为response返回。

fetchEvent.respondWith(
  // Promise that resolves to a Response.
​)

Parameters:A Promise for a Response.

上面的sw.js只是一个最基本的serviceWorker,在日常工作中,我们还需要考虑更新。

四、Service Worker更新

(一)自动更新

this.addEventListener('install',function(event){
    this.skipWaiting();
});
this.addEventListener('activate', function (event) {
    this.clients.claim();
});

skipWaiting(): 强制等待中的service worker跳过等待成为激活的service worker。虽然该方法在任何时候都是可以调用的,但是只有在新安装的service worker仍然处于等待状态时才会起作用;所以在install事件里面调用是非常常见的。与clients.claim()一起调用以确保更新当前的client和其他激活的clients。
clients.claim(): 允许一个激活的service worker将其设置为其他同scope下的clients的controller。该方法会触发要被该service worker控制的其他任何clients的navigator.serviceWorker上的"controllerchange"事件。
当一个service worker初始注册时并不会使用该service worker,直到下次加载页面时。该方法会让这些页面直接被控制,注意,这将导致你的service worker将控制定期加载的页面,也有可能控制其他service worker加载的页面。

(二)手动更新

手动更新主要是调用在index.html注册serviceWorker时的registration的update()方法:ServiceWorkerRegistration.update();
它会获取worker的脚本URL,如果新的worker与当前的worker并不是完全相同的(byte-by-byte identical)则安装新的worker;如果前一次worker获取发生在24小时之前,则worker的获取将绕过任何浏览器缓存。

navigator.serviceWorker.register('./sw.js').then(function(registration){
   registration.update();
});

参考学习链接:
https://developer.mozilla.org...
https://developers.google.cn/...
https://lavas.baidu.com/pwa/o...
https://segmentfault.com/a/11...

阅读 1.9k
15 声望
1 粉丝
0 条评论
15 声望
1 粉丝
宣传栏