菜单

Javascript:用Service Worker做一个离线网页应用

2019年4月16日 - JavaScript

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

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

原来的书文出处:
人人网FED博客   

在上一篇《自身是如何让网址用上HTML伍Manifest》介绍了怎么用Manifest做三个离线网页应用,结果被大面积网上好友戏弄说这一个事物已经被deprecated,移出web标准了,现在被ServiceWorker代替了,不管如何,Manifest的片段盘算依然得以借用的。小编又将网址晋级到了ServiceWorker,假设是用Chrome等浏览器就用ServiceWorker做离线缓存,借使是Safari浏览器就如故用Manifest,读者能够展开这几个网址https://fed.renren.com感受一下,断网也是能符合规律展开。

参考资料
MDN — Service Worker
API

Service Workers: an
Introduction

服务办事线程生命周期
瑟维斯 Worker Cookbook(搜罗了ServiceWorker的1部分施行例子)
理解 Service
Workers

1. 什么是Service Worker

Service Worker是谷歌(Google)发起的兑现PWA(Progressive Web
App)的叁个生死攸关角色,PWA是为着缓解守旧Web 应用软件的通病:

(一)没有桌面入口

(贰)不能够离线使用

(3)没有Push推送

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

manbetx2.0手机版 1

ServiceWorker是在后台运营的一条服务Worker线程,上海教室小编开了三个标签页,所以展现了多个Client,不过不管开多少个页面都唯有二个Worker在负责管理。这些Worker的做事是把部分能源缓存起来,然后拦截页面包车型地铁央浼,先看下缓存库里有未有,借使有的话就从缓存里取,响应200,反之未有的话就走平常的请求。具体来讲,ServiceWorker结合Web App Manifest能成功以下工作(那也是PWA的检查测试正式):

manbetx2.0手机版 2

包涵可以离线使用、断网时重回200、能唤起用户把网址加多一个图标到桌面上等。

本人提醒

2. 瑟维斯 Worker的支撑意况

Service Worker近来唯有Chrome/Firfox/Opera帮助:

manbetx2.0手机版 3

Safari和艾德ge也在预备援助Service Worker,由于ServiceWorker是谷歌(Google)宗旨的一项正式,对于生态相比封闭的Safari来说也是迫于时势起首准备协助了,在Safari
TP版本,能够看出:

manbetx2.0手机版 4

在尝试功用(Experimental Features)里早就有ServiceWorker的菜单项了,只是固然展开也是不可能用,会唤起您还并未兑现:

manbetx2.0手机版 5

但随便如何,至少表达Safari已经准备帮忙ServiceWorker了。此外还足以观察在当年20一七年11月宣告的Safari
1一.0.1版本现已支撑WebRTC了,所以Safari依然三个升华的子女。

Edge也准备帮助,所以Service Worker的前景13分美好。

  1. 使用范围
    Service Worker由于权力异常高,只接济https协议恐怕localhost。
    个人感到Github
    Pages
    是三个很出彩的演习场馆。
  2. 储备知识
    ServiceWorker大批量使用Promise,不打听的请移步:Javascript:Promise对象基础

3. 使用Service Worker

ServiceWorker的选择套路是首先登场记3个Worker,然后后台就会运行一条线程,能够在那条线程运行的时候去加载一些财富缓存起来,然后监听fetch事件,在那个事件里拦截页面包车型客车乞请,先看下缓存里有没有,如若有直接重临,不然符合规律加载。也许是壹初叶不缓存,每一个财富请求后再拷贝1份缓存起来,然后下2遍呼吁的时候缓存里就有了。

兼容性

manbetx2.0手机版,(壹)注册一个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事件运营呢?因为你要卓殊运转3个线程,运维之后您恐怕还会让它去加载能源,那么些都以索要占用CPU和带宽的,大家理应保险页面能健康加载完,然后再起步我们的后台线程,不可能与正规的页面加载产生竞争,这么些在低级移动设备意义比较大。

再有有个别亟需注意的是ServiceWorker和Cookie同样是有Path路线的定义的,假使您设定叁个cookie假若叫time的path=/page/A,在/page/B那些页面是不能赢获得那些cookie的,即便设置cookie的path为根目录/,则怀有页面都能博获得。类似地,假若注册的时候利用的js路线为/page/sw.js,那么那个ServiceWorker只可以管理/page路线下的页面和能源,而无法处理/api路线下的,所以一般把ServiceWorker注册到5星级目录,如上边代码的”/sw-三.js”,那样这一个ServiceWorker就能接管页面包车型客车具有财富了。

manbetx2.0手机版 6

(二)瑟维斯 Worker安装和激活

登记完事后,ServiceWorker就会实行安装,这一年会触发install事件,在install事件之中能够缓存一些能源,如下sw-3.js:

JavaScript

const CACHE_NAME = “fed-cache”; this.add伊夫ntListener(“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);
        })
    );
});

经过地点的操作,创造和加多了3个缓存库叫fed-cache,如下Chrome调控台所示:

manbetx2.0手机版 7

瑟维斯Worker的API基本上都是再次回到Promise对象制止堵塞,所以要用Promise的写法。上边在安装ServiceWorker的时候就把首页的伸手给缓存起来了。在ServiceWorker的周转环境之中它有2个caches的全局对象,这几个是缓存的输入,还有一个常用的clients的全局对象,三个client对应1个标签页。

在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-三.js”这么些已经登记了,就不会再登记了,进而不会触发install和active事件,因为脚下ServiceWorker已经是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都1模同样才是同样的能源,能够设定第1个参数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,借使跨域的财富援救COCRUISERS,那么能够把request的mod改成cors。尽管请求失利了,如404照旧是逾期等等的,那么也平昔回到response让主页面处理,不然的话表明加载成功,把这一个response克朗多个放到cache里面,然后再重回response给主页面线程。注意能舒缓存里的能源壹般只好是GET,通过POST获取的是无法缓存的,所以要做个剖断(当然你也能够手动把request对象的method改成get),还有把有些私有不期望缓存的财富也做个判定。

那样假诺用户张开过二回页面,ServiceWorker就安装好了,他刷新页面恐怕打开第2个页面包车型客车时候就可见把请求的能源11做缓存,包蕴图形、CSS、JS等,只要缓存里有了随便用户在线只怕离线都能够常常访问。那样我们自然会有1个标题,这么些缓存空间到底有多大?上一篇我们提到Manifest也总算地点存款和储蓄,PC端的Chrome是伍Mb,其实这个说法在新本子的Chrome已经不精确了,在Chrome
六1本子可以看出本地存款和储蓄的长空和使用情形:

manbetx2.0手机版 8

其间Cache Storage是指ServiceWorker和Manifest占用的空间尺寸和,上海体育场面能够看来总的空间大小是20GB,大致是unlimited,所以基本上不用顾忌缓存会不够用。

一、 生命周期

个人感觉先清楚一下它的生命周期很要紧!以前查资料的时候,大多篇章一上来就监听install事件、waiting事件、activate事件……反正自个儿是1脸懵逼。

manbetx2.0手机版 9

Service Worker的生命周期

(4)cache html

上面第(三)步把图片、js、css缓存起来了,可是如若把页面html也缓存了,例如把首页缓存了,就会有一个啼笑皆非的难点——ServiceWorker是在页面注册的,然则未来获取页面包车型大巴时候是从缓存取的,每便都以一样的,所以就产生不可能创新ServiceWorker,如产生sw-五.js,不过PWA又要求大家能缓存页面html。那如何是好吧?谷歌的开垦者文书档案它只是提到会存在那一个主题素材,但并未有认证怎么化解那么些标题。那个的难点的缓解将供给大家要有1个建制能分晓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 U福睿斯L(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,借使是的话就去发个请求获取2个文件,依据那些文件的剧情决定是或不是需求删除缓存,这些立异的函数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文件,1个页面会对应二个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: []}}

个中根本有二个updateTime的字段,即便地方内部存款和储蓄器未有这几个页面包车型地铁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: 壹就表示那是一个翻新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也不会缓存了。

再有1种更新是用户更新的,例如用户发布了评论,要求在页面文告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);
    }
});

据他们说分歧的音讯类型调分化的回调函数,假设是一的话就是剔除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缓存,假诺3者都用上了会怎样呢?

会以Service Worker为优先,因为ServiceWorker把请求拦截了,它首先做拍卖,假使它缓存Curry有个别话一贯回到,未有的话平常请求,就也就是尚未ServiceWorker了,这一年就到了Manifest层,Manifest缓存里假若局地话就取那几个缓存,假若未有的话就也正是尚未Manifest了,于是就会从Http缓存里取了,假若Http缓存里也未曾就会发请求去赢得,服务端根据Http的etag只怕Modified
Time可能会回到30四 Not
Modified,不然平常再次回到200和数量内容。那就是整2个得到的进度。

故而若是既用了Manifest又用ServiceWorker的话应该会招致同1个能源存了五次。然则能够让帮忙ServiceWorker的浏览器采取Service Worker,而不帮助的利用Manifest.

2. Installing

在installing状态中,SW 脚本中的 install
事件被实行。在力所能及决定客户端此前,install
事件让我们有空子缓存大家要求的享有内容。

譬如,我们得以先缓存一张图纸,那么当SW调控客户端之后,客户点击该链接的图片,大家就足以用SW捕获请求,直接回到该图片的缓存。

若事件中有 event.waitUntil() 方法,则 installing
事件会直接等到该措施中的 Promise 完结之后才会中标;若 Promise
被拒,则设置战败,瑟维斯 Worker 直接进入丢弃(redundant)状态。

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

瞩目那里说的是其余三个Manifest,这么些Manifest是1个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展开就会像张开2个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缓存:
manbetx2.0手机版 10把start_url指向的页面用ServiceWorker缓存起来,那样当用户用Chrome浏览器打开这一个网页的时候,Chrome就会在底层弹一个提示,询问用户是或不是把那些网页增添到桌面,假使点“增多”就会变动四个桌面Logo,从那些Logo点进去就如展开三个App同样。感受如下:

manbetx2.0手机版 11

相比难堪的是Manifest近期唯有Chrome支持,并且只可以在安卓系统上使用,IOS的浏览器不能增添三个桌面图标,因为IOS未有开放这种API,不过本身的Safari却又是足以的。

综上,本文介绍了怎么用瑟维斯 Worker结合Manifest做3个PWA离线Web
APP,首若是用ServiceWorker调控缓存,由于是写JS,比较灵活,还是能够与页面实行通讯,此外通过请求页面包车型地铁翻新时间来判定是还是不是供给更新html缓存。ServiceWorker的包容性不是特意好,可是前景相比光明,浏览器都在备选协理。现阶段得以构成offline
cache的Manifest做离线应用。

连带阅读:

  1. 怎么要把网址升级到HTTPS
  2. 哪些把网址升级到http/二
  3. 自家是怎么着让网址用上HTML5Manifest

1 赞 1 收藏
评论

manbetx2.0手机版 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

归来3个Navigator目的,该目的容易的话就是允许大家获得我们用户代理(浏览器)的一部分新闻。比如,浏览器的合法名称,浏览器的本子,网络连接处境,设备地方新闻等等。

2. navigator.serviceWorker

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

上面的代码中第2判别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钦命1个成效域:
.register('service-worker.js', { scope: './controlled' });

叁. 怎么在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事件。

Service 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;
                }
            );
        })
    );
});

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

manbetx2.0手机版 13

offline1

然后点击“Vue”的链接:

manbetx2.0手机版 14

offline2

能够看出缓存中多了一张后缀为.png的图片。
SW缓存了大家的新请求!

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

manbetx2.0手机版 15

offline3

下一场,刷新页面。

manbetx2.0手机版 16

offline4

照旧得以访问页面。

相关文章

发表评论

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

网站地图xml地图