菜单

选用 Service Worker 做一个 PWA 离线网页应用

2019年10月5日 - XML

利用 Service Worker 做二个 PWA 离线网页应用

2017/10/09 · JavaScript
· PWA, Service
Worker

原著出处:
人人网FED博客   

在上一篇《本身是怎么着让网址用上HTML5
Manifest
》介绍了怎么用Manifest做三个离线网页应用,结果被相近网上朋友捉弄说这么些事物已经被deprecated,移出web标准了,未来被ServiceWorker替代了,不管怎么样,Manifest的有的思虑还能够借用的。作者又将网址晋级到了ServiceWorker,若是是用Chrome等浏览器就用ServiceWorker做离线缓存,借使是Safari浏览器就依然用Manifest,读者能够展开这几个网址https://fed.renren.com感受一下,断网也是能健康张开。

参照他事他说加以考察资料
MDN — Service Worker
API

Service Workers: an
Introduction

劳动职业线程生命周期
Service Worker Cookbook(搜聚了ServiceWorker的某个施行例子)
理解 Service
Workers

1. 什么是Service Worker

Service Worker是谷歌(Google)倡导的实现PWA(Progressive Web
App)的多个第一角色,PWA是为着缓和古板Web APP的缺欠:

(1)未有桌面入口

(2)不能够离线使用

(3)没有Push推送

那Service Worker的具体表现是怎么着的吧?如下图所示:

图片 1

ServiceWorker是在后台运行的一条服务Worker线程,上航海用图书馆笔者开了四个标签页,所以体现了多少个Client,可是不管开多少个页面都只有贰个Worker在担负管理。那几个Worker的做事是把一些财富缓存起来,然后拦截页面包车型大巴央求,先看下缓存Curry有未有,要是某个话就从缓存里取,响应200,反之未有的话就走正规的呼吁。具体来讲,ServiceWorker结合Web App Manifest能做到以下专门的职业(那也是PWA的检查评定规范):

图片 2

总结能够离线使用、断网时再次来到200、能唤醒客户把网址增添二个Logo到桌面上等。

本人提醒

2. Service Worker的支撑意况

Service Worker最近唯有Chrome/Firfox/Opera帮忙:

图片 3

Safari和Edge也在备选协理Service Worker,由于ServiceWorker是Google宗旨的一项正式,对于生态相比较密封的Safari来讲也是迫于时局开端筹算扶助了,在Safari
TP版本,能够看来:

图片 4

在试验功能(Experimental Features)里曾经有ServiceWorker的菜单项了,只是纵然张开也是无法用,会提示您还尚无落到实处:

图片 5

但不论怎么,起码注解Safari已经计划帮助ServiceWorker了。别的还能看出在二〇一七年二零一七年十月公告的Safari
11.0.1本子现已支撑WebRTC了,所以Safari照旧三个上扬的孩子。

Edge也计划援助,所以Service Worker的前景特别美好。

  1. 运用范围
    Service Worker由于权力相当高,只扶助https合同恐怕localhost。
    个人认为Github
    Pages
    是一个极美的演练场地。
  2. 储备知识
    ServiceWorker大批量使用Promise,不打听的请移步:Javascript:Promise对象基础

3. 使用Service Worker

ServiceWorker的应用套路是先注册一个Worker,然后后台就能运行一条线程,可以在那条线程运维的时候去加载一些能源缓存起来,然后监听fetch事件,在这些事件里拦截页面包车型地铁伸手,先看下缓存里有未有,纵然有直接再次回到,不然平常加载。也许是一开端不缓存,种种能源诉求后再拷贝一份缓存起来,然后下一遍呼吁的时候缓存里就有了。

兼容性

(1)注册叁个Service Worker

Service Worker对象是在window.navigator里面,如下代码:

JavaScript

window.addEventListener(“load”, function() { console.log(“Will the
service worker register?”); navigator.serviceWorker.register(‘/sw-3.js’)
.then(function(reg){ console.log(“Yes, it did.”); }).catch(function(err)
{ console.log(“No it didn’t. This happened: “, err) }); });

1
2
3
4
5
6
7
8
9
window.addEventListener("load", function() {
    console.log("Will the service worker register?");
    navigator.serviceWorker.register(‘/sw-3.js’)
    .then(function(reg){
        console.log("Yes, it did.");
    }).catch(function(err) {
        console.log("No it didn’t. This happened: ", err)
    });
});

在页面load完之后注册,注册的时候传二个js文件给它,这么些js文件便是ServiceWorker的周转条件,假设不可能打响注册的话就能够抛非凡,如Safari
TP即使有其一目的,但是会抛格外无法利用,就能够在catch里面处理。这里有个难点是为何供给在load事件运营呢?因为你要十二分运行三个线程,运行之后您只怕还有恐怕会让它去加载财富,那些都是急需占用CPU和带宽的,大家应有有限支撑页面能健康加载完,然后再开行我们的后台线程,无法与健康的页面加载发生竞争,那么些在低级移动设备意义非常大。

再有某个内需潜心的是ServiceWorker和Cookie同样是有Path路线的定义的,即使你设定贰个cookie要是叫time的path=/page/A,在/page/B那几个页面是不能够赢获得那几个cookie的,假若设置cookie的path为根目录/,则怀有页面都能赢得到。类似地,假如注册的时候利用的js路线为/page/sw.js,那么这一个ServiceWorker只好管理/page路线下的页面和财富,而不可见管理/api路径下的,所以平时把ServiceWorker注册到超级目录,如上边代码的”/sw-3.js”,那样那些ServiceWorker就能够接管页面包车型大巴装有能源了。

图片 6

(2)Service Worker安装和激活

挂号完事后,ServiceWorker就能够进展设置,那个时候会触发install事件,在install事件之中能够缓存一些能源,如下sw-3.js:

JavaScript

const CACHE_NAME = “fed-cache”; this.addEventListener(“install”,
function(event) { this.skipWaiting(); console.log(“install service
worker”); // 创造和开采叁个缓存库 caches.open(CACHE_NAME); // 首页 let
cacheResources = [“https://fed.renren.com/?launcher=true"\];
event.waitUntil( // 伏乞能源并增加到缓存里面去
caches.open(CACHE_NAME).then(cache => {
cache.addAll(cacheResources); }) ); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const CACHE_NAME = "fed-cache";
this.addEventListener("install", function(event) {
    this.skipWaiting();
    console.log("install service worker");
    // 创建和打开一个缓存库
    caches.open(CACHE_NAME);
    // 首页
    let cacheResources = ["https://fed.renren.com/?launcher=true"];
    event.waitUntil(
        // 请求资源并添加到缓存里面去
        caches.open(CACHE_NAME).then(cache => {
            cache.addAll(cacheResources);
        })
    );
});

由此地点的操作,成立和增多了一个缓存库叫fed-cache,如下Chrome调控台所示:

图片 7

ServiceWorker的API基本上都以回去Promise对象制止堵塞,所以要用Promise的写法。下边在安装ServiceWorker的时候就把首页的乞请给缓存起来了。在瑟维斯Worker的运营条件之中它有二个caches的大局对象,那么些是缓存的输入,还大概有一个常用的clients的大局对象,一个client对应二个标签页。

在ServiceWorker里面能够利用fetch等API,它和DOM是割裂的,未有windows/document对象,不能直接操作DOM,无法直接和页面交互,在ServiceWorker里面不能得知当前页面展开了、当前页面包车型的士url是怎样,因为三个ServiceWorker管理当前展开的多少个标签页,能够通过clients知道全部页面包车型客车url。还可能有能够经过postMessage的不二秘籍和主页面相互传送音信和数量,进而做些调节。

install完事后,就能触发瑟维斯 Worker的active事件:

JavaScript

this.addEventListener(“active”, function(event) { console.log(“service
worker is active”); });

1
2
3
this.addEventListener("active", function(event) {
    console.log("service worker is active");
});

ServiceWorker激活之后就能够监听fetch事件了,大家盼望每得到八个能源就把它缓存起来,就不用像上一篇涉嫌的Manifest须要先生成一个列表。

您恐怕会问,当本身刷新页面的时候不是又再一次注册安装和激活了四个ServiceWorker?就算又调了一回注册,但并不会重新挂号,它开采”sw-3.js”那些已经登记了,就不会再登记了,进而不会触发install和active事件,因为脚下瑟维斯Worker已是active状态了。当须要更新ServiceWorker时,如形成”sw-4.js”,也许改变sw-3.js的文书内容,就能重新挂号,新的ServiceWorker会先install然后步向waiting状态,等到重启浏览器时,老的ServiceWorker就能够被替换掉,新的ServiceWorker步入active状态,借使不想等到再也启航浏览器能够像上边一样在install里面调skipWaiting:

JavaScript

this.skipWaiting();

1
this.skipWaiting();

Service Worker的包容性

(3)fetch资源后cache起来

通常来讲代码,监听fetch事件做些管理:

JavaScript

this.addEventListener(“fetch”, function(event) { event.respondWith(
caches.match(event.request).then(response => { // cache hit if
(response) { return response; } return
util.fetchPut(event.request.clone()); }) ); });

1
2
3
4
5
6
7
8
9
10
11
12
this.addEventListener("fetch", function(event) {
    event.respondWith(
        caches.match(event.request).then(response => {
            // cache hit
            if (response) {
                return response;
            }
            return util.fetchPut(event.request.clone());
        })
    );
});

先调caches.match看一下缓存里面是或不是有了,若是有平素回到缓存里的response,不然的话平常诉求能源并把它内置cache里面。放在缓存里能源的key值是Request对象,在match的时候,要求哀告的url和header都一点差距也未有才是均等的能源,能够设定第一个参数ignoreVary:

JavaScript

caches.match(event.request, {ignoreVary: true})

1
caches.match(event.request, {ignoreVary: true})

表示如若央浼url一样就以为是同三个财富。

地点代码的util.fetchPut是那样实现的:

JavaScript

let util = { fetchPut: function (request, callback) { return
fetch(request).then(response => { // 跨域的财富直接return if
(!response || response.status !== 200 || response.type !== “basic”) {
return response; } util.putCache(request, response.clone()); typeof
callback === “function” && callback(); return response; }); }, putCache:
function (request, resource) { // 后台不要缓存,preview链接也无须缓存 if
(request.method === “GET” && request.url.indexOf(“wp-admin”) < 0 &&
request.url.indexOf(“preview_id”) < 0) {
caches.open(CACHE_NAME).then(cache => { cache.put(request,
resource); }); } } };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let util = {
    fetchPut: function (request, callback) {
        return fetch(request).then(response => {
            // 跨域的资源直接return
            if (!response || response.status !== 200 || response.type !== "basic") {
                return response;
            }
            util.putCache(request, response.clone());
            typeof callback === "function" && callback();
            return response;
        });
    },
    putCache: function (request, resource) {
        // 后台不要缓存,preview链接也不要缓存
        if (request.method === "GET" && request.url.indexOf("wp-admin") < 0
              && request.url.indexOf("preview_id") < 0) {
            caches.open(CACHE_NAME).then(cache => {
                cache.put(request, resource);
            });
        }
    }
};

亟需专心的是跨域的能源不能够缓存,response.status会重返0,假诺跨域的能源辅助CO奥迪Q5S,那么能够把request的mod改成cors。假若必要失败了,如404照旧是逾期等等的,那么也一直回到response让主页面处理,不然的话表明加载成功,把那么些response克隆二个停放cache里面,然后再回来response给主页面线程。注意能减缓存里的能源日常只好是GET,通过POST获取的是无法缓存的,所以要做个推断(当然你也得以手动把request对象的method改成get),还恐怕有把一部分个体不指望缓存的财富也做个判别。

如此那般借使顾客张开过一回页面,ServiceWorker就安装好了,他刷新页面也许展开第三个页面包车型地铁时候就可见把要求的能源一一做缓存,富含图形、CSS、JS等,只要缓存里有了随意客商在线可能离线都能够符合规律访谈。那样我们当然会有五个主题材料,这些缓存空间到底有多大?上一篇大家提到Manifest也好不轻巧地方存款和储蓄,PC端的Chrome是5Mb,其实这一个说法在新本子的Chrome已经不确切了,在Chrome
61版本能够看到地面存储的上空和选拔情形:

图片 8

里头Cache Storage是指ServiceWorker和Manifest占用的半空中尺寸和,上航海用体育场合能够看来总的空间大小是20GB,大约是unlimited,所以基本上不用驰念缓存会非常不够用。

一、 生命周期

民用以为先清楚一下它的生命周期很关键!在此以前查资料的时候,很多篇章一上来就监听install事件、waiting事件、activate事件……反正自个儿是一脸懵逼。

图片 9

Service Worker的生命周期

(4)cache html

地方第(3)步把图片、js、css缓存起来了,可是纵然把页面html也缓存了,举例把首页缓存了,就能够有一个两难的标题——ServiceWorker是在页面注册的,但是今后赢得页面包车型地铁时候是从缓存取的,每一趟都是同样的,所以就招致力所不及创新ServiceWorker,如产生sw-5.js,不过PWA又要求大家能缓存页面html。那怎么做吧?谷歌(Google)的开荒者文书档案它只是提到会存在那些标题,但并从未证实怎么化解那么些难点。那几个的主题素材的消除将在求大家要有多个机制能清楚html更新了,进而把缓存里的html给替换掉。

Manifest更新缓存的体制是去看Manifest的文件内容有没有发生变化,假若爆发变化了,则会去创新缓存,ServiceWorker也是依据sw.js的文本内容有未有发生变化,大家得以借鉴这么些思想,即使央求的是html并从缓存里抽出来后,再发个央求获取多个文书看html更新时间是还是不是发生变化,即使产生变化了则表达发生转移了,进而把缓存给删了。所以能够在服务端通过调节这些文件进而去立异顾客端的缓存。如下代码:

JavaScript

this.add伊芙ntListener(“fetch”, function(event) { event.respondWith(
caches.match(event.request).then(response => { // cache hit if
(response) { //假使取的是html,则看发个乞求看html是不是更新了 if
(response.headers.get(“Content-Type”).indexOf(“text/html”) >= 0) {
console.log(“update html”); let url = new UTiggoL(event.request.url);
util.updateHtmlPage(url, event.request.clone(), event.clientId); }
return response; } return util.fetchPut(event.request.clone()); }) );
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
this.addEventListener("fetch", function(event) {
 
    event.respondWith(
        caches.match(event.request).then(response => {
            // cache hit
            if (response) {
                //如果取的是html,则看发个请求看html是否更新了
                if (response.headers.get("Content-Type").indexOf("text/html") >= 0) {
                    console.log("update html");
                    let url = new URL(event.request.url);
                    util.updateHtmlPage(url, event.request.clone(), event.clientId);
                }
                return response;
            }
 
            return util.fetchPut(event.request.clone());
        })
    );
});

透过响应头header的content-type是不是为text/html,纵然是的话就去发个央浼获取二个文本,依照那些文件的内容决定是还是不是须要删除缓存,那几个创新的函数util.updateHtmlPage是如此达成的:

JavaScript

let pageUpdateTime = { }; let util = { updateHtmlPage: function (url,
htmlRequest) { let pageName = util.getPageName(url); let jsonRequest =
new Request(“/html/service-worker/cache-json/” + pageName + “.sw.json”);
fetch(jsonRequest).then(response => { response.json().then(content
=> { if (pageUpdateTime[pageName] !== content.updateTime) {
console.log(“update page html”); // 假使有立异则另行赢得html
util.fetchPut(htmlRequest); pageUpdateTime[pageName] =
content.updateTime; } }); }); }, delCache: function (url) {
caches.open(CACHE_NAME).then(cache => { console.log(“delete cache “

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
let pageUpdateTime = {
 
};
let util = {
    updateHtmlPage: function (url, htmlRequest) {
        let pageName = util.getPageName(url);
        let jsonRequest = new Request("/html/service-worker/cache-json/" + pageName + ".sw.json");
        fetch(jsonRequest).then(response => {
            response.json().then(content => {
                if (pageUpdateTime[pageName] !== content.updateTime) {
                    console.log("update page html");
                    // 如果有更新则重新获取html
                    util.fetchPut(htmlRequest);
                    pageUpdateTime[pageName] = content.updateTime;
                }
            });
        });
    },
    delCache: function (url) {
        caches.open(CACHE_NAME).then(cache => {
            console.log("delete cache " + url);
            cache.delete(url, {ignoreVary: true});
        });
    }
};

代码先去赢得一个json文件,二个页面会对应三个json文件,那么些json的内容是那样的:

JavaScript

{“updateTime”:”10/2/2017, 3:23:57 PM”,”resources”: {img: [], css:
[]}}

1
{"updateTime":"10/2/2017, 3:23:57 PM","resources": {img: [], css: []}}

里面根本有一个update提姆e的字段,借使本地内部存款和储蓄器未有这些页面包车型地铁updateTime的数据依旧是和新星updateTime不均等,则再次去获得html,然后放到缓存里。接着必要文告页面线程数据发生变化了,你刷新下页面吗。那样就无须等客户刷新页面技艺卓有成效了。所以当刷新完页面后用postMessage文告页面:

JavaScript

let util = { postMessage: async function (msg) { const allClients =
await clients.matchAll(); allClients.forEach(client =>
client.postMessage(msg)); } }; util.fetchPut(htmlRequest, false,
function() { util.postMessage({type: 1, desc: “html found updated”, url:
url.href}); });

1
2
3
4
5
6
7
8
9
let util = {
    postMessage: async function (msg) {
        const allClients = await clients.matchAll();
        allClients.forEach(client => client.postMessage(msg));
    }
};
util.fetchPut(htmlRequest, false, function() {
    util.postMessage({type: 1, desc: "html found updated", url: url.href});
});

并规定type: 1就表示那是一个立异html的新闻,然后在页面监听message事件:

JavaScript

if(“serviceWorker” in navigator) {
navigator.serviceWorker.addEventListener(“message”, function(event) {
let msg = event.data; if (msg.type === 1 && window.location.href ===
msg.url) { console.log(“recv from service worker”, event.data);
window.location.reload(); } }); }

1
2
3
4
5
6
7
8
9
if("serviceWorker" in navigator) {
    navigator.serviceWorker.addEventListener("message", function(event) {
        let msg = event.data;
        if (msg.type === 1 && window.location.href === msg.url) {
            console.log("recv from service worker", event.data);
            window.location.reload();
        }  
    });
}

接下来当大家须要更新html的时候就立异json文件,那样客户就能够阅览最新的页面了。大概是当客户重新起动浏览器的时候会招致ServiceWorker的运营内部存款和储蓄器都被清空了,即存款和储蓄页面更新时间的变量被清空了,那一年也会重复伏乞页面。

急需专注的是,要把那么些json文件的http
cache时间设置成0,那样浏览器就不会缓存了,如下nginx的配备:

JavaScript

location ~* .sw.json$ { expires 0; }

1
2
3
location ~* .sw.json$ {
    expires 0;
}

因为那个文件是急需实时获取的,不可能被缓存,firefox暗中认可会缓存,Chrome不会,加上http缓存时间为0,firefox也不会缓存了。

再有一种更新是客商更新的,举例顾客公布了评价,供给在页面通告service
worker把html缓存删了重复获得,这是多少个扭曲的消息通告:

JavaScript

if (“serviceWorker” in navigator) {
document.querySelector(“.comment-form”).addEventListener(“submit”,
function() { navigator.serviceWorker.controller.postMessage({ type: 1,
desc: “remove html cache”, url: window.location.href} ); } }); }

1
2
3
4
5
6
7
8
9
10
if ("serviceWorker" in navigator) {
    document.querySelector(".comment-form").addEventListener("submit", function() {
            navigator.serviceWorker.controller.postMessage({
                type: 1,
                desc: "remove html cache",
                url: window.location.href}
            );
        }
    });
}

Service Worker也监听message事件:

JavaScript

const messageProcess = { // 删除html index 1: function (url) {
util.delCache(url); } }; let util = { delCache: function (url) {
caches.open(CACHE_NAME).then(cache => { console.log(“delete cache “

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const messageProcess = {
    // 删除html index
    1: function (url) {
        util.delCache(url);
    }
};
 
let util = {
    delCache: function (url) {
        caches.open(CACHE_NAME).then(cache => {
            console.log("delete cache " + url);
            cache.delete(url, {ignoreVary: true});
        });
    }
};
 
this.addEventListener("message", function(event) {
    let msg = event.data;
    console.log(msg);
    if (typeof messageProcess[msg.type] === "function") {
        messageProcess[msg.type](msg.url);
    }
});

基于差异的音信类型调分化的回调函数,就算是1的话正是剔除cache。顾客发布完争持后会触发刷新页面,刷新的时候缓存已经被删了就能够再一次去哀求了。

如此就一挥而就了实时更新的主题素材。

1. Parsed

SW是一个JS文件,若是大家要利用三个SW(ServiceWorker),那么大家要求在大家的js代码中登记它,类似于:
navigator.serviceWorker.register('/sw-1.js')

于今并无需知道那几个办法各样部分的事无巨细含义,只要驾驭我们今后在为咱们的网页注册叁个SW就可以了。

能够见到我们传入的参数是一个JS文件的门路,当浏览器奉行到这里的时候,就能到相应的门道下载该公文,然后对该脚本进行分析,假如下载或然解析战败,那么这么些SW就能被舍弃。

一经条分缕析成功了,那就到了parsed状态。能够扩充上边包车型地铁专门的学问了。

4. Http/Manifest/Service Worker三种cache的关系

要缓存能够选用三种花招,使用Http
Cache设置缓存时间,也足以用Manifest的Application Cache,还足以用ServiceWorker缓存,若是三者都用上了会怎么着呢?

会以Service Worker为先行,因为ServiceWorker把央浼拦截了,它首先做拍卖,假使它缓存Curry一些话平素回到,未有的话平常乞求,就相当于尚未ServiceWorker了,那一年就到了Manifest层,Manifest缓存里如若某些话就取这么些缓存,若无的话就一定于尚未Manifest了,于是就会从Http缓存里取了,假设Http缓存里也尚未就能发诉求去获得,服务端依据Http的etag恐怕Modified
Time可能会回去304 Not
Modified,不然日常再次来到200和多少内容。那正是整一个赢得的长河。

就此假如既用了Manifest又用ServiceWorker的话应该会促成同一个资源存了五次。可是足以让帮衬ServiceWorker的浏览器采取Service Worker,而不扶助的施用Manifest.

2. Installing

在installing状态中,SW 脚本中的 install
事件被奉行。在能力所能达到支配顾客端从前,install
事件让大家有时机缓存我们供给的享有剧情。

举例,我们能够先缓存一张图纸,那么当SW调控顾客端之后,顾客点击该链接的图片,大家就能够用SW捕获央求,直接回到该图片的缓存。

若事件中有 event.waitUntil() 方法,则 installing
事件会直接等到该方法中的 Promise 实现之后才会中标;若 Promise
被拒,则设置失败,Service Worker 直接进去吐弃(redundant)状态。

5. 选拔Web App Manifest增添桌面入口

留意这里说的是其余三个Manifest,这么些Manifest是一个json文件,用来放网址icon名称等音讯以便在桌面增添三个Logo,以及制造一种张开那几个网页就像张开App一样的法力。下面平素说的Manifest是被舍弃的Application
Cache的Manifest。

本条Maifest.json文件能够如此写:

JavaScript

{ “short_name”: “人人FED”, “name”: “人人网FED,静心于前者技艺”,
“icons”: [ { “src”: “/html/app-manifest/logo_48.png”, “type”:
“image/png”, “sizes”: “48×48” }, { “src”:
“/html/app-manifest/logo_96.png”, “type”: “image/png”, “sizes”: “96×96”
}, { “src”: “/html/app-manifest/logo_192.png”, “type”: “image/png”,
“sizes”: “192×192” }, { “src”: “/html/app-manifest/logo_512.png”,
“type”: “image/png”, “sizes”: “512×512” } ], “start_url”:
“/?launcher=true”, “display”: “standalone”, “background_color”:
“#287fc5”, “theme_color”: “#fff” }

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
26
27
28
29
30
{
  "short_name": "人人FED",
  "name": "人人网FED,专注于前端技术",
  "icons": [
    {
      "src": "/html/app-manifest/logo_48.png",
      "type": "image/png",
      "sizes": "48×48"
    },
    {
      "src": "/html/app-manifest/logo_96.png",
      "type": "image/png",
      "sizes": "96×96"
    },
    {
      "src": "/html/app-manifest/logo_192.png",
      "type": "image/png",
      "sizes": "192×192"
    },
    {
      "src": "/html/app-manifest/logo_512.png",
      "type": "image/png",
      "sizes": "512×512"
    }
  ],
  "start_url": "/?launcher=true",
  "display": "standalone",
  "background_color": "#287fc5",
  "theme_color": "#fff"
}

icon须要预备八种尺码,最大供给512px *
512px的,那样Chrome会自动去选取合适的图形。假如把display改成standalone,从转换的Logo展开就能够像展开八个App同样,未有浏览器地址栏那多少个东西了。start_url内定展开之后的输入链接。

接下来加多两个link标签指向这些manifest文件:

JavaScript

<link rel=”manifest” href=”/html/app-manifest/manifest.json”>

1
<link rel="manifest" href="/html/app-manifest/manifest.json">

与此相类似组合Service Worker缓存:
图片 10把start_url指向的页面用ServiceWorker缓存起来,那样当客户用Chrome浏览器张开那些网页的时候,Chrome就能在尾部弹三个提醒,询问客商是或不是把这一个网页加多到桌面,假诺点“增多”就能够变动贰个桌面Logo,从那些图标点进去就好像张开二个App一样。感受如下:

图片 11

比较为难的是Manifest这段日子唯有Chrome补助,何况只可以在安卓系统上运用,IOS的浏览器不能加多二个桌面Logo,因为IOS未有开放这种API,可是本人的Safari却又是能够的。

综上,本文介绍了怎么用Service Worker结合Manifest做三个PWA离线Web
APP,首假如用瑟维斯Worker调整缓存,由于是写JS,比较灵活,还足以与页面实行通讯,其他通过央求页面包车型地铁立异时间来决断是不是须要创新html缓存。ServiceWorker的宽容性不是专程好,不过前景相比光明,浏览器都在备选援助。现阶段得以结合offline
cache的Manifest做离线应用。

有关阅读:

  1. 缘何要把网址进级到HTTPS
  2. 如何把网址进级到http/2
  3. 本身是何许让网址用上HTML5
    Manifest

1 赞 1 收藏
评论

图片 12

3. Installed / Waiting

借使设置成功,Service Worker
步向installed(waiting)状态。在此景况中,它是四个得力的但未曾激活的
worker。它从未放入 document 的操纵,确切来讲是在等候着从当下 worker
接手。

高居 Waiting 状态的 SW,在以下之一的情形下,会被触发 Activating 状态。

4. Activating

地处 activating 状态之间,SW 脚本中的 activate
事件被施行。大家普通在 activate 事件中,清理 cache
中的文件(清除旧Worker的缓存文件)。

SW激活退步,则一向进去吐弃(redundant)状态。

5. Activated

假诺激活成功,SW
步入激活状态。在此意况中,SW起头接管调整客商端,并得以拍卖fetch(捕捉诉求)、
push(消息推送)、 sync(同步事件)等功用性事件:

// sw.js

self.addEventListener('fetch', function(event) {  
  // Do stuff with fetch events
});

self.addEventListener('message', function(event) {  
  // Do stuff with postMessages received from document
}); 
......

6. Redundant 废弃

Service Worker 只怕以下之一的开始和结果而被遗弃(redundant)——

 
咱俩早已驾驭了SW的生命周期了,那么今后就从头来做三个离线应用。

大家只兑现最简便的效能:顾客每发送几个http恳求,大家就用SW捕获那几个须求,然后在缓存里找是还是不是缓存了那一个央浼对应的响应内容,假诺找到了,就把缓存中的内容重临给主页面,不然再发送诉求给服务器。

二、 register 注册

率先要注册三个SW,在index.js文件中:

// index.js

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    // 注册一个service worker,这个例子中worker的路径是根目录中的,所以这个worker可以缓存这个项目中任意文件。如果目录是‘/js/sw.js‘,那么只能缓存目录'/js'下的文件
    // 参数registration存储了本次注册的一些相关信息
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      // Registration was successful
      // registration.scope 返回的是这个service worker的作用域
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }).catch(function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

知识点:

1. window.navigator

回来一个Navigator指标,该目的简单来说正是允许大家取得我们顾客代理(浏览器)的一部分音信。比方,浏览器的官方名称,浏览器的版本,网络连接景况,设备地方消息等等。

2. navigator.serviceWorker

归来三个
ServiceWorkerContainer对象,该对象允许大家对SW实行登记、删除、更新和通讯。

地点的代码中第一判别navigator是否有serviceWorker质量(存在的话表示浏览器协理SW),假使存在,那么通过navigator.serviceWorker.register()(也就是ServiceWorkerContainer.register())来注册三个新的SW,.register()收受五个
路径 作为第二个参数。

ServiceWorkerContainer.register()回到四个Promise,所以能够用.then()
.catch()来展开持续管理。

3. SW的成效域

假若未有一些名该SW的成效域,那么它的私下认可作用域正是其所在的目录。
比如,.register('/sw.js')中,sw.js在根目录中,所以功能域是成套项指标文件。

若果是那般:.register('/controlled/sw.js'),sw.js的功用域是/controlled。

咱俩能够手动为SW钦定多少个功效域:
.register('service-worker.js', { scope: './controlled' });

3. 为啥在load事件中打开挂号

何以供给在load事件运行呢?因为你要相当运转贰个线程,运营之后您只怕还或许会让它去加载能源,那个都以必要占用CPU和带宽的,大家应当保险页面能正常加载完,然后再开发银行大家的后台线程,不能够与健康的页面加载发生竞争,那个在低档移动设备意义十分大。

三、install 安装

小编们早就注册好了SW,倘使 sw.js
下载並且分析成功,我们的SW就进来安装阶段了,这时候会触发install事件。我们日常在install事件中缓存我们想要缓存的静态财富,供SW调整主页面之后接纳:

// sw.js

var CACHE_NAME = 'my-site-cache-v1'; // cache对象的名字
var urlsToCache = [ // 想要缓存的文件的数组
  '/',
  '/styles/main.css',
  '/script/main.js'
];

// 如果所有文件都成功缓存,则将安装成功
self.addEventListener('install', function(event) {
  // 执行安装步骤
  // ExtendableEvent.waitUntil()方法延长了安装过程,直到其传回的Promise被resolve之后才会安装成功
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        // console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

知识点:

1. cache

Cache是同意大家管理缓存的
Request
/
Response
对象对的接口,可以经过那几个接口增加和删除查改 Request / Response 对。

地方代码中cache.addAll(urlsToCache)表示把数组中的文件都缓存在内部存款和储蓄器中。
详细询问请戳 :
Cache

2. caches

caches是一个CacheStorage目的,提供多少个可被访问的命名Cache对象的目录,维护字符串名称到对应Cache对象的光彩夺目。

大家能够经过该指标张开某叁个一定的Cache对象,大概查看该列表中是或不是盛名字为“xxx”的Cache对象,也足以去除某多个Cache对象。

四、activate 激活

咱俩的SW已经安装成功了,它能够图谋调节客商端并拍卖 push 和 sync
等效果事件了,那时,大家收获二个 activate 事件。

// sw.js

self.addEventListener("activate", function(event) {
    console.log("service worker is active");
});

一旦SW安装成功并被激活,那么调控台会打字与印刷出”service worker is active”。

假若大家是在更新SW的动静下,此时应有还应该有八个旧的SW在劳作,那时大家的新SW就不会被激活,而是步向了
“Waiting” 状态。

大家需求关闭此网址的有着标签页来关闭旧SW,使新的SW激活。或然手动激活。

那么activate事件能够用来干什么吧?假若咱们明天换了贰个新的SW,新SW须要缓存的静态能源和旧的差别,那么大家就要求破除旧缓存。

何以吗?因为三个域能用的缓存空间是个其他,若无科学处理缓存数据,导致数据过大,浏览器会帮大家删除数据,那么大概会误删我们想要留在缓存中的数据。

这几个将来会详细讲,未来只须要精通activate事件能用来排除旧缓存旧能够了。

五、 fetch事件

近日我们的SW已经激活了,那么能够初阶捕获网络央浼,来加强网址的性质了。

当网页发出供给的时候,会触发fetch事件。

瑟维斯 Workers能够监听该事件,’拦截’ 要求,并调整回到内容 ————
是重临缓存的数额,还是发送央浼,再次回到服务器响应的多少。

上边包车型地铁代码中,SW会检查测量试验缓存中是或不是有客商想要的剧情,即使有,就回到缓存中的内容。不然再发送网络需要。

// sw.js

self.addEventListener('fetch', event => {
    const { request } = event; // 获取request
    const findResponsePromise = caches.open(CACHE_NAME)
    // 在match的时候,需要请求的url和header都一致才是相同的资源
    // caches.match(event.request, {ignoreVary: true}) 表示只要请求url相同就认为是同一个资源。
    .then(cache => cache.match(request)) // 查看cache对象中是否有匹配的项
    .then(response => {
        if (response) { // 如果response不为空,则返回response,否则发送网络请求
            return response;
        }

        return fetch(request);
    });
    // event.respondWith 是一个 FetchEvent 对象中的特殊方法,用于将请求的响应发送回浏览器。它接收一个对响应(或网络错误)resolve 后的 Promise 对象作为参数。
    event.respondWith(findResponsePromise);
});

箭头函数真的很契合用于Promise对象,省略了一批的functionreturn一言九鼎字,望着清爽多了……

至于缓存攻略
昔不最近的行使场景必要采纳差别的缓存战术。

譬喻,小红希望她的网址在在线的时候总是回到缓存中的内容,然后在后台更新缓存;在离线的时候,重返缓存的内容。

比方,小明希望她的网址能够在在线的时候回来最新的响应内容,离线的时候再回去缓存中的内容。
……
假使想要研商一下各个缓存计策,能够参见上面包车型客车材质,这里就不详述了,不然小说就成裹脚布了……
The Service Worker
Cookbook

离线指南
ServiceWorker最棒试行

而是,既然标题是“做八个离线网页应用”,那大家就做多个最简单易行的缓存计谋:假诺缓存中保存着伏乞的从头到尾的经过,则赶回缓存中的内容,不然,乞请新剧情,并缓存新内容。

self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.match(event.request)
        .then(response => {
            // Cache hit - return response
            if (response) {
                return response;
            }
            // 克隆请求。因为请求是一个“stream”,只能用一次。但我们需要用两次,一次用来缓存,一次给浏览器抓取内容,所以需要克隆
            var fetchRequest = event.request.clone();
            // 返回请求的内容
            return fetch(fetchRequest).then(
                response => {
                    // 检查是否为有效的响应。basic表示同源响应,也就是说,这意味着,对第三方资产的请求不会添加到缓存。
                    if (!response || response.status !== 200 || response.type !== 'basic') {
                        return response;
                    }
                    // 同request,response是一个“stream”,只能用一次,但我们需要用两次,一次用来缓存一个返回给浏览器,所以需要克隆。
                    var responseToCache = response.clone();
                    // 缓存新请求
                    caches.open(CACHE_NAME)
                        .then(cache => cache.put(event.request, responseToCache));
                    return response;
                }
            );
        })
    );
});

 
姣好啦!大家简陋的离线应用!
开发页面,看一下缓存中有何样内容:

图片 13

offline1

下一场点击“Vue”的链接:

图片 14

offline2

能够见到缓存中多了一张后缀为.png的图片。
SW缓存了咱们的新央浼!

展开chrome的开拓者工具,点击offline,使标签页处于离线状态:

图片 15

offline3

然后,刷新页面。

图片 16

offline4

照例得以访谈页面。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图