离线网页选拔,大英帝国卫报的本性离线页面是那般做的

by admin on 2019年2月2日

Service Worker初体验

2016/01/06 · JavaScript
· Service Worker

原文出处: AlloyTeam   

在二〇一四年,W3C发布了service worker的草案,service
worker提供了重重新的能力,使得web app拥有与native
app相同的离线体验、音信推送体验。
service worker是一段脚本,与web
worker一样,也是在后台运行。作为一个独自的线程,运行环境与普通脚本差别,所以不能够直接插手web交互行为。native
app可以完结离线使用、新闻推送、后台自动更新,service
worker的面世是多亏为了使得web app也可以有所类似的力量。

 

service worker可以:

  1. 后台音信传递
  2. 网络代理,转载呼吁,伪造响应
  3. 离线缓存
  4. 信息推送
  5.  … …

本文以资源缓存为例,说圣元下service worker是如何行事的。

连不上网?英帝国卫报的天性离线页面是那样做的

2015/11/20 · HTML5 · Service
Worker,
离线页面

本文由 伯乐在线 –
Erucy
翻译,离线网页选拔,大英帝国卫报的本性离线页面是那般做的。weavewillg
校稿。未经许可,禁止转发!
英文出处:Oliver
Ash。欢迎参加翻译组。

咱们是怎样行使 service worker 来为 theguardian.com
营造一个自定义的离线页面。

必发88 1

theguardian.com 的离线页面。插图:奥利弗 Ash

您正在朝着公司途中的大巴里,在手机上开辟了
Guardian
应用。大巴被隧道包围着,可是那么些利用可以正常运行,即使没有网络连接,你也能获取完全的法力,除了浮现的内容可能有点旧。即使您品味在网站上也如此干,可惜它完全无法加载:

必发88 2

安卓版 Chrome 的离线页面

Chrome 中的这一个彩蛋,很三个人都不了解离线网页选拔,大英帝国卫报的本性离线页面是那般做的。》

Chrome
在离线页面上有个藏匿的游乐(桌面版上按空格键,手机版上点击那只恐龙),那有些能减轻一点您的苦恼。可是大家可以做得更好。

Service
workers
允许网站小编拦截自己站点的具有网络请求,那也就表示大家可以提供完善的离线体验,似乎原生应用相同。在
Guardian
网站,大家如今上线了一个自定义的离线体验效果。当用户离线的时候,他们会看到一个涵盖
Guardian
标识的页面,下边带有一个大约的离线提示,还有一个填字游戏,他们可以在伺机网络连接的时候玩玩这几个找点乐子。那篇博客解释了大家是何等打造它的,不过在上马之前,你可以先自己摸索看。

Service Worker入门

2015/03/26 · JavaScript
· Service Worker

原文出处: Matt
Gaunt   译文出处:[w3ctech

  • 十年踪迹]()   

原生App拥有Web应用一般所不拥有的富离线体验,定时的沉默更新,音信通告推送等成效。而新的Serviceworkers标准让在Web App上所有这么些职能成为可能。

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

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

原稿出处:
人人网FED博客   

在上一篇《本人是哪些让网站用上HTML5
Manifest》介绍了怎么用Manifest做一个离线网页应用,结果被大面积网友吐槽说那么些事物已经被deprecated,移出web标准了,现在被ServiceWorker替代了,不管如何,Manifest的局地思考还能借用的。小编又将网站升级到了ServiceWorker,假若是用Chrome等浏览器就用ServiceWorker做离线缓存,即使是Safari浏览器就照旧用Manifest,读者可以打开这几个网站感受一下,断网也是能健康打开。

生命周期

先来看一下一个service worker的周转周期

必发88 3
上图是service
worker生命周期,出处

图中得以看出,一个service worker要经历以下进度:

  1.  安装

2.
 激活,激活成功将来,打开chrome://inspect/#service-workers可以查阅到近期运行的service
worker

必发88 4

  1. 监听fetch和message事件,上边三种事件会展开简要描述

  2. 销毁,是或不是销毁由浏览器决定,若是一个service
    worker长时间不采用仍然机器内存有数,则可能会销毁那个worker

试试看

你需求一个支撑 Service
Worker 和 fetch
API 的浏览器。截至到本文编写时只有Chrome(手机版和桌面版)同时接济那三种 API(译者注:Opera
近期也协理那两边),不过 Firefox
很快就要扶助了(在每一日更新的版本中曾经支撑了),除去 Safari
之外的所有浏览器也都在尝试。其它,service worker 只好登记在应用了
HTTPS 的网站上,theguardian.com
已经先导渐渐搬迁到 HTTPS,所以大家只可以在网站的 HTTPS
部分提供离线体验。就当下来说,我们选用了 开发者博客 作为我们用来测试的地点。所以只要你是在大家网站的 开发者博客 部分阅读那篇文章的话,很幸运。

当您利用协理的浏览器访问我们的 开发者博客 中的页面的时候,一切就准备妥当了。断开你的网络连接,然后刷新一下页面。借使你协调没条件尝试的话,能够看一下那段 演示摄像(译者注:需梯子)。

Service Worker 是什么?

一个 service worker
是一段运行在浏览器后台进度里的剧本,它独立于当下页面,提供了那么些不必要与web页面交互的法力在网页背后悄悄执行的力量。在以后,基于它可以已毕音讯推送,静默更新以及地理围栏等劳动,但是当前它首先要具有的效能是阻碍和拍卖互联网请求,包蕴可编程的响应缓存管理。

怎么说那几个API是一个至极棒的API呢?因为它使得开发者可以接济特别好的离线体验,它赋予开发者完全控制离线数据的能力。

在service worker提出从前,此外一个提供开发者离线体验的API叫做App
Cache。可是App
Cache有些局限性,例如它可以很简单地化解单页应用的标题,不过在多页应用上会很艰难,而Serviceworkers的出现正是为理解决App Cache的痛点。

上边详细说一下service worker有何须要小心的地方:

  • 它是JavaScript
    Worker,所以它不可能从来操作DOM。不过service
    worker可以透过postMessage与页面之间通信,把新闻公告给页面,若是须要的话,让页面自己去操作DOM。
  • Serviceworker是一个可编程的网络代理,允许开发者控制页面上拍卖的互联网请求。
  • 在不被利用的时候,它会自己终止,而当它再度被用到的时候,会被再一次激活,所以您不可能依赖于service
    worker的onfecth和onmessage的处理函数中的全局状态。要是你想要保存一些持久化的新闻,你可以在service
    worker里使用IndexedDB API。
  • Serviceworker大批量使用promise,所以只要你不打听怎么是promise,那您要求先读书这篇文章。

1. 什么是Service Worker

Service Worker是谷歌倡导的完成PWA(Progressive Web
App)的一个生死攸关角色,PWA是为着解决传统Web APP的老毛病:

(1)没有桌面入口

(2)无法离线使用

(3)没有Push推送

那Service Worker的具体表现是何等的呢?如下图所示:

必发88 5

ServiceWorker是在后台启动的一条服务Worker线程,上图我开了多少个标签页,所以显得了五个Client,然而不管开几个页面都唯有一个Worker在负责管理。这几个Worker的劳作是把一些资源缓存起来,然后拦截页面的乞求,先看下缓存库里有没有,即使有些话就从缓存里取,响应200,反之没有的话就走正常的伸手。具体来说,ServiceWorker结合Web App Manifest能完结以下工作(那也是PWA的检测标准):

必发88 6

概括可以离线使用、断网时回来200、能唤起用户把网站添加一个图标到桌面上等。

fetch事件

在页面发起http请求时,service
worker可以透过fetch事件拦截请求,并且付诸自己的响应。
w3c提供了一个新的fetch
api,用于代替XMLHttpRequest,与XMLHttpRequest最大不一致有两点:

1.
fetch()方法再次来到的是Promise对象,通过then方法进行连接调用,减弱嵌套。ES6的Promise在改为标准之后,会愈来愈便利开发人员。

2. 提供了Request、Response对象,假设做过后端开发,对Request、Response应该相比熟练。前端要倡导呼吁能够由此url发起,也足以选用Request对象发起,而且Request能够复用。可是Response用在何地呢?在service
worker出现从前,前端确实不会友善给协调发音信,可是有了service
worker,就足以在阻碍请求之后根据需求发回自己的响应,对页面而言,这一个平凡的伸手结果并没有区分,那是Response的一处选用。

下边是在中,小编运用fetch
api通过fliker的公然api获取图片的例证,注释中详细表明了每一步的机能:

JavaScript

/* 由于是get请求,直接把参数作为query string传递了 */ var URL =
”;
function fetchDemo() { // fetch(url,
option)帮衬四个参数,option中得以安装header、body、method音讯fetch(URL).then(function(response) { // 通过promise
对象得到相应内容,并且将响应内容按照json格式转成对象,json()方法调用之后再次来到的照旧是promise对象
// 也得以把内容转化成arraybuffer、blob对象 return response.json();
}).then(function(json) { // 渲染页面 insertPhotos(json); }); }
fetchDemo();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 由于是get请求,直接把参数作为query string传递了 */
var URL = ‘https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=your_api_key&format=json&nojsoncallback=1&tags=penguins’;
 
function fetchDemo() {
  // fetch(url, option)支持两个参数,option中可以设置header、body、method信息
  fetch(URL).then(function(response) {
    // 通过promise 对象获得相应内容,并且将响应内容按照json格式转成对象,json()方法调用之后返回的依然是promise对象
    // 也可以把内容转化成arraybuffer、blob对象
    return response.json();
  }).then(function(json) {
    // 渲染页面
    insertPhotos(json);
  });
}
 
fetchDemo();

fetch
api与XMLHttpRequest比较,尤其简洁,并且提供的功力更宏观,资源得到情势比ajax更优雅。包容性方面:chrome
42从头援救,对于旧浏览器,可以透过法定维护的polyfill支持。

办事规律

经过一段简单的
JavaScript,大家得以提示浏览器在用户访问页面的时候马上登记我们团结的
service worker。如今支撑 service worker
的浏览器很少,所以为了幸免不当,我们须求采纳特性检测。

JavaScript

if (navigator.serviceWorker) {
navigator.serviceWorker.register(‘/service-worker.js’); }

1
2
3
if (navigator.serviceWorker) {
    navigator.serviceWorker.register(‘/service-worker.js’);
}

Service worker
安装事件的一部分,我们得以应用 新的缓存
API 来缓存大家网站中的种种内容,比如
HTML、CSS 和
JavaScript:

JavaScript

var staticCacheName = ‘static’; var version = 1; function updateCache()
{ return caches.open(staticCacheName + version) .then(function (cache) {
return cache.addAll([ ‘/offline-page.html’, ‘/assets/css/main.css’,
‘/assets/js/main.js’ ]); }); }; self.addEventListener(‘install’,
function (event) { event.waitUntil(updateCache()); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var staticCacheName = ‘static’;
var version = 1;
 
function updateCache() {
    return caches.open(staticCacheName + version)
        .then(function (cache) {
            return cache.addAll([
                ‘/offline-page.html’,
                ‘/assets/css/main.css’,
                ‘/assets/js/main.js’
            ]);
        });
};
 
self.addEventListener(‘install’, function (event) {
    event.waitUntil(updateCache());
});

当安装到位后,service worker
可以监听和操纵 fetch
事件,让大家可以完全控制之后网站中生出的持有互连网请求。

JavaScript

self.addEventListener(‘fetch’, function (event) {
event.respondWith(fetch(event.request)); });

1
2
3
self.addEventListener(‘fetch’, function (event) {
    event.respondWith(fetch(event.request));
});

在此处大家有很利索的半空中可以表达,比如上边那一个节骨眼,可以经过代码来生成大家温馨的哀告响应:

JavaScript

self.addEventListener(‘fetch’, function (event) { var response = new
Response(‘<h1>Hello, World!</h1>’, { headers: {
‘Content-Type’: ‘text/html’ } }); event.respondWith(response); });

1
2
3
4
5
self.addEventListener(‘fetch’, function (event) {
    var response = new Response(‘&lt;h1&gt;Hello, World!&lt;/h1&gt;’,
        { headers: { ‘Content-Type’: ‘text/html’ } });
    event.respondWith(response);
});

还有这些,就算在缓存中找到了请求相应的缓存,大家可以直接从缓存中回到它,要是没找到的话,再通过网络获得响应内容:

JavaScript

self.addEventListener(‘fetch’, function (event) { event.respondWith(
caches.match(event.request) .then(function (response) { return response
|| fetch(event.request); }) ); });

1
2
3
4
5
6
7
8
self.addEventListener(‘fetch’, function (event) {
    event.respondWith(
        caches.match(event.request)
            .then(function (response) {
                return response || fetch(event.request);
            })
    );
});

那就是说大家怎么着利用那个作用来提供离线体验吧?

第一,在 service worker
安装进程中,大家须求把离线页面必要的 HTML 和资源文件通过 service worker
缓存下来。在缓存中,大家加载了友好付出的 填字游戏 的
React应用 页面。之后,我们会阻碍所有访问
theguardian.com
互连网请求,包括网页、以及页面中的资源文件。处理那一个请求的逻辑大致如下:

  1. 当大家检测到传播请求是指向大家的 HTML
    页面时,我们连年会想要提供新型的始末,所以大家会尝试把那一个请求通过互连网发送给服务器。

    1. 当我们从服务器获得了响应,就足以直接重返那几个响应。
    2. 假诺网络请求抛出了相当(比如因为用户掉线了),大家捕获那么些丰裕,然后利用缓存的离线
      HTML 页面作为响应内容。
  2. 再不,当我们检测到请求的不是 HTML
    的话,大家会从缓存中摸索响应的呼吁内容。

    1. 比方找到了缓存内容,我们可以一贯回到缓存的始末。
    2. 要不,大家会尝试把这么些请求通过网络发送给服务器。

在代码中,大家运用了 新的缓存
API(它是 Service Worker API 的一有些)以及
fetch
作用(用于转移网络请求),如下所示:

JavaScript

var doesRequestAcceptHtml = function (request) { return
request.headers.get(‘Accept’) .split(‘,’) .some(function (type) { return
type === ‘text/html’; }); }; self.addEventListener(‘fetch’, function
(event) { var request = event.request; if
(doesRequestAcceptHtml(request)) { // HTML pages fallback to offline
page event.respondWith( fetch(request) .catch(function () { return
caches.match(‘/offline-page.html’); }) ); } else { // Default fetch
behaviour // Cache first for all other requests event.respondWith(
caches.match(request) .then(function (response) { return response ||
fetch(request); }) ); } });

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
var doesRequestAcceptHtml = function (request) {
    return request.headers.get(‘Accept’)
        .split(‘,’)
        .some(function (type) { return type === ‘text/html’; });
};
 
self.addEventListener(‘fetch’, function (event) {
    var request = event.request;
    if (doesRequestAcceptHtml(request)) {
        // HTML pages fallback to offline page
        event.respondWith(
            fetch(request)
                .catch(function () {
                    return caches.match(‘/offline-page.html’);
                })
        );
    } else {
        // Default fetch behaviour
        // Cache first for all other requests
        event.respondWith(
            caches.match(request)
                .then(function (response) {
                    return response || fetch(request);
                })
        );
    }
});

就只要求这么多!theguardian.com
上的 怀有代码都是在 GitHub
上开源 的,所以你可以去那儿查看大家的
service worker
的一体化版本,或者直接从生产条件上访问

大家有丰盛的理由为这么些新的浏览器技术欢呼喝彩,因为它可以用来让你的网站像后天的原生应用相同,拥有完美的离线体验。未来当
theguardian.com 完全迁移到 HTTPS
之后,离线页面的基本点性会明显增多,大家可以提供进一步健全的离线体验。设想一下您在上下班途中互联网很差的时候访问
theguardian.com,你会看出专门为您订制的个性化内容,它们是在您此前访问网站时由浏览器缓存下来的。它在装置进度中也不会发出任何不便,你所急需的只是造访那一个网站而已,不像原生应用,还索要用户有一个用到公司的账号才能设置。Serviceworker
同样可以帮助我们进步网站的加载速度,因为网站的框架可以被保证地缓存下来,就像是原生应用相同。

假使你对 service worker
很感兴趣,想要明白越多内容的话,开发者 Matt
Gaunt(Chrome的忠诚协理者)写了一篇越发详细地 介绍 Service
Worker的文章。

打赏帮助我翻译越多好小说,谢谢!

打赏译者

Service Worker的生命周期

Service worker拥有一个完全独立于Web页面的生命周期。

要让一个service
worker在您的网站上生效,你要求先在你的网页中登记它。注册一个service
worker之后,浏览器会在后台默默启动一个service worker的设置进度。

在装置进程中,浏览器会加载并缓存一些静态资源。如若拥有的文书被缓存成功,service
worker就设置成功了。假若有其余文件加载或缓存败北,那么安装进程就会破产,service
worker就不可以被激活(也即没能安装成功)。如若爆发这么的标题,别担心,它会在下次再品尝安装。

当安装已毕后,service
worker的下一步是激活,在这一阶段,你还能提高一个service
worker的本子,具体内容大家会在后面讲到。

在激活之后,service
worker将接管所有在自己管辖域范围内的页面,不过假使一个页面是刚刚注册了service
worker,那么它这一遍不会被接管,到下几回加载页面的时候,service
worker才会生效。

当service
worker接管了页面之后,它可能有二种境况:要么被终止以节约内存,要么会处理fetch和message事件,那七个事件分别发生于一个互联网请求出现仍然页面上发送了一个音信。

下图是一个简化了的service worker初次安装的生命周期:

必发88 7

2. Service Worker的帮衬情状

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

必发88 8

Safari和Edge也在备选支持Service Worker,由于ServiceWorker是谷歌(谷歌)基本的一项正式,对于生态比较封闭的Safari来说也是迫于时局开首准备帮衬了,在Safari
TP版本,可以观察:

必发88 9

在实验功用(Experimental Features)里早已有ServiceWorker的菜单项了,只是就算打开也是不可能用,会提醒您还尚未落成:

必发88 10

但不论是什么样,至少声明Safari已经准备协理ServiceWorker了。其余还足以看来在今年前年六月布告的Safari
11.0.1版本现已支撑WebRTC了,所以Safari照旧一个上扬的子女。

Edge也准备支持,所以Service Worker的前景分外美好。

message事件

页面和serviceWorker之间可以透过posetMessage()方法发送新闻,发送的音讯能够通过message事件接收到。

那是一个双向的进程,页面可以发新闻给service worker,service
worker也可以发送新闻给页面,由于那么些特点,可以将service
worker作为中间纽带,使得一个域名照旧子域名下的三个页面可以任意通讯。

此地是一个小的页面之间通信demo

打赏支持自己翻译越多好小说,谢谢!

必发88 11

1 赞 收藏
评论

在大家初步写码此前

从这个品种地址拿到chaches
polyfill。

这个polyfill支持CacheStorate.match,Cache.add和Cache.addAll,而现在Chrome
M40实现的Cache
API还一直不协助那么些方法。

将dist/serviceworker-cache-polyfill.js放到你的网站中,在service
worker中经过importScripts加载进来。被service
worker加载的剧本文件会被自动缓存。

JavaScript

importScripts(‘serviceworker-cache-polyfill.js’);

1
importScripts(‘serviceworker-cache-polyfill.js’);

需要HTTPS

在开发阶段,你可以由此localhost使用service
worker,但是只要上线,就必要您的server协理HTTPS。

你可以透过service
worker胁迫连接,伪造和过滤响应,卓殊逆天。就算你可以约束自己不干坏事,也会有人想干坏事。所以为了防止万一别人使坏,你只好在HTTPS的网页上注册service
workers,那样大家才可以预防加载service
worker的时候不被歹徒篡改。(因为service
worker权限很大,所以要防微杜渐它自身被坏人篡改利用——译者注)

Github
Pages正巧是HTTPS的,所以它是一个不错的后天实验田。

假如您想要让您的server支持HTTPS,你须求为你的server获得一个TLS证书。不一样的server安装方法不一致,阅读支持文档并透过Mozilla’s
SSL config
generator摸底最佳实践。

3. 使用Service Worker

ServiceWorker的利用套路是先注册一个Worker,然后后台就会启动一条线程,可以在那条线程启动的时候去加载一些资源缓存起来,然后监听fetch事件,在那一个事件里拦截页面的呼吁,先看下缓存里有没有,假若有直接重返,否则正常加载。或者是一初步不缓存,每个资源请求后再拷贝一份缓存起来,然后下一遍呼吁的时候缓存里就有了。

采用service workder缓存文件

下边介绍一个行使service worker缓存离线文件的事例
未雨绸缪index.js,用于注册service-worker

JavaScript

if (navigator.serviceWorker) {
navigator.serviceWorker.register(‘service-worker.js’).then(function(registration)
{ console.log(‘service worker 注册成功’); }).catch(function (err) {
console.log(‘servcie worker 注册败北’) }); }

1
2
3
4
5
6
7
if (navigator.serviceWorker) {
    navigator.serviceWorker.register(‘service-worker.js’).then(function(registration) {
        console.log(‘service worker 注册成功’);
    }).catch(function (err) {
        console.log(‘servcie worker 注册失败’)
    });
}

在上述代码中,注册了service-worker.js作为当前路线下的service
worker。由于service
worker的权杖很高,所有的代码都需若是安全可相信的,所以唯有https站点才得以应用service
worker,当然localhost是一个特例。
挂号甘休,现在上马写service-worker.js代码。
根据前面的生命周期图,在一个新的service
worker被登记之后,首先会触发install事件,在service-workder.js中,可以因此监听install事件进行部分开首化工作,或者什么也不做。
因为我们是要缓存离线文件,所以可以在install事件中开始缓存,可是只是将文件加到caches缓存中,真正想让浏览器采取缓存文件须求在fetch事件中阻止

JavaScript

var cacheFiles = [ ‘about.js’, ‘blog.js’ ];
self.addEventListener(‘install’, function (evt) { evt.waitUntil(
caches.open(‘my-test-cahce-v1’).then(function (cache) { return
cache.addAll(cacheFiles); }) ); });

1
2
3
4
5
6
7
8
9
10
11
var cacheFiles = [
    ‘about.js’,
    ‘blog.js’
];
self.addEventListener(‘install’, function (evt) {
    evt.waitUntil(
        caches.open(‘my-test-cahce-v1’).then(function (cache) {
            return cache.addAll(cacheFiles);
        })
    );
});

首先定义了需求缓存的文件数组cacheFile,然后在install事件中,缓存这么些文件。
evt是一个Install伊芙nt对象,继承自Extendable伊芙nt,其中的waitUntil()方法接收一个promise对象,直到那一个promise对象成功resolve之后,才会持续运行service-worker.js。
caches是一个CacheStorage对象,使用open()方法打开一个缓存,缓存通过名称举行区分。
赢得cache实例之后,调用addAll()方法缓存文件。

那样就将文件添加到caches缓存中了,想让浏览器接纳缓存,还索要拦截fetch事件

JavaScript

// 缓存图片 self.add伊芙ntListener(‘fetch’, function (evt) {
evt.respondWith( caches.match(evt.request).then(function(response) { if
(response) { return response; } var request = evt.request.clone();
return fetch(request).then(function (response) { if (!response &&
response.status !== 200 &&
!response.headers.get(‘Content-type’).match(/image/)) { return response;
} var responseClone = response.clone();
caches.open(‘my-test-cache-v1’).then(function (cache) {
cache.put(evt.request, responseClone); }); return response; }); }) ) });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 缓存图片
self.addEventListener(‘fetch’, function (evt) {
    evt.respondWith(
        caches.match(evt.request).then(function(response) {
            if (response) {
                return response;
            }
            var request = evt.request.clone();
            return fetch(request).then(function (response) {
                if (!response && response.status !== 200 && !response.headers.get(‘Content-type’).match(/image/)) {
                    return response;
                }
                var responseClone = response.clone();
                caches.open(‘my-test-cache-v1’).then(function (cache) {
                    cache.put(evt.request, responseClone);
                });
                return response;
            });
        })
    )
});

经过监听fetch事件,service worker可以回去自己的响应。

第一检缓存中是不是业已缓存了那些请求,假诺有,就一贯回到响应,就减弱了一遍网络请求。否则由service
workder发起请求,这时的service workder起到了一个中档代理的法力。

service worker请求的历程通过fetch
api完毕,得到response对象未来进行过滤,查看是还是不是是图片文件,假设不是,就一贯再次来到请求,不会缓存。

假如果图形,要先复制一份response,原因是request或者response对象属于stream,只可以采纳两次,之后一份存入缓存,另一份发送给页面。
那就是service worker的强硬之处:拦截请求,伪造响应。fetch
api在此间也起到了很大的功能。

 

service
worker的翻新很简短,只要service-worker.js的文本内容有更新,就会接纳新的本子。不过有少数要注意:旧缓存文件的解除、新文件的缓存要在activate事件中展开,因为可能旧的页面还在利用从前的缓存文件,清除之后会失掉意义。

 

在首先使用service worker的历程中,也赶上了一部分题材,上面是其中多个

至于小编:Erucy

必发88 12

早就的SharePoint喵星程序猿(暂时还挂着微软MVP的名头),现在的Azure/.Net/MongoDB/Cordova/前端程序猿,偶尔写随笔
个人主页 ·
我的稿子 ·
46 ·
  

必发88 13

使用Service Worker

前些天我们有了polyfill,并且搞定了HTTPS,让我们看看究竟怎么用service
worker。

(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就能接管页面的具备资源了。

题目1. 运作时刻

service
worker并不是一直在后台运行的。在页面关闭后,浏览器可以持续保持service
worker运行,也可以关闭service
worker,那有赖于与浏览器自己的一颦一笑。所以不要定义一些全局变量,例如下面的代码(来自):

JavaScript

var hitCounter = 0; this.addEventListener(‘fetch’, function(event) {
hitCounter++; event.respondWith( new Response(‘Hit number ‘ +
hitCounter) ); });

1
2
3
4
5
6
7
8
var hitCounter = 0;
 
this.addEventListener(‘fetch’, function(event) {
  hitCounter++;
  event.respondWith(
    new Response(‘Hit number ‘ + hitCounter)
  );
});

归来的结果或者是从未规律的:1,2,1,2,1,1,2….,原因是hitCounter并没有直接留存,若是浏览器关闭了它,下次开行的时候hitCounter就赋值为0了
那样的事情导致调试代码困难,当您更新一个service
worker将来,只有在打开新页面将来才可能利用新的service
worker,在调试进度中平常等上一两分钟才会动用新的,比较抓狂。

怎么注册和装置service worker

要设置service
worker,你须要在你的页面上登记它。这些手续告诉浏览器你的service
worker脚本在哪儿。

JavaScript

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

1
2
3
4
5
6
7
8
9
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是不是可用,纵然可用,service
worker /sw.js 被注册。

如若这些service worker已经被登记过,浏览器会自动忽略下边的代码。

有一个内需专门表明的是service
worker文件的门径,你一定注意到了在那么些例子中,service
worker文件被放在这几个域的根目录下,那意味service
worker和网站同源。换句话说,这几个service
work将会收到那个域下的装有fetch事件。假设本身将service
worker文件注册为/example/sw.js,那么,service worker只能收到/example/路径下的fetch事件(例如: /example/page1/, /example/page2/)。

近日您可以到 chrome://inspect/#service-workers 检查service worker是否对你的网站启用了。

必发88 14

当service
worker第一版被完毕的时候,你也得以在chrome://serviceworker-internals中查看,它很有用,通过它可以最直观地熟悉service worker的生命周期,不过这个功能很快就会被移到chrome://inspect/#service-workers中。

你会意识这些成效能够很方便地在一个效仿窗口中测试你的service
worker,那样你可以关闭和另行打开它,而不会潜移默化到你的新窗口。任何成立在模仿窗口中的注册服务和缓存在窗口被关闭时都将荡然无存。

(2)Service 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 = [“];
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控制台所示:

必发88 15

ServiceWorker的API基本上都是重返Promise对象幸免堵塞,所以要用Promise的写法。下面在设置ServiceWorker的时候就把首页的哀求给缓存起来了。在ServiceWorker的周转条件之中它有一个caches的全局对象,这几个是缓存的入口,还有一个常用的clients的大局对象,一个client对应一个标签页。

在ServiceWorker里面可以运用fetch等API,它和DOM是与世隔膜的,没有windows/document对象,不可以直接操作DOM,不可能直接和页面交互,在ServiceWorker里面不可以获知当前页面打开了、当前页面的url是何等,因为一个瑟维斯Worker管理当前打开的多少个标签页,可以透过clients知道所有页面的url。还有可以因此postMessage的艺术和主页面相互传递消息和数量,进而做些控制。

install完事后,就会触发Service 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事件,因为眼下ServiceWorker已经是active状态了。当须要更新ServiceWorker时,如变成”sw-4.js”,或者改变sw-3.js的文件内容,就会再也注册,新的ServiceWorker会先install然后进入waiting状态,等到重启浏览器时,老的ServiceWorker就会被轮换掉,新的ServiceWorker进入active状态,借使不想等到再一次启航浏览器可以像上边一样在install里面调skipWaiting:

JavaScript

this.skipWaiting();

1
this.skipWaiting();

标题2. 权力太大

当service worker监听fetch事件之后,对应的哀告都会透过service
worker。通过chrome的network工具,可以看来此类请求会标注:from service
worker。若是service
worker中出现了难点,会招致所有请求败北,包蕴一般的html文件。所以service
worker的代码品质、容错性一定要很好才能担保web app正常运转。

 

参照小说:

1. 

2. 

3. 

4. 

5. 

1 赞 3 收藏
评论

必发88 16

瑟维斯 Worker的装置步骤

在页面上已毕登记手续之后,让我们把注意力转到service
worker的脚本里来,在这些中,大家要做到它的安装步骤。

在最中央的例子中,你要求为install事件定义一个callback,并操纵哪些文件你想要缓存。

JavaScript

// The files we want to cache var urlsToCache = [ ‘/’,
‘/styles/main.css’, ‘/script/main.js’ ]; // Set the callback for the
install step self.addEventListener(‘install’, function(event) { //
Perform install steps });

1
2
3
4
5
6
7
8
9
10
11
// The files we want to cache
var urlsToCache = [
  ‘/’,
  ‘/styles/main.css’,
  ‘/script/main.js’
];
 
// Set the callback for the install step
self.addEventListener(‘install’, function(event) {
    // Perform install steps
});

在大家的install callback中,大家须要实施以下步骤:

  1. 打开一个缓存
  2. 缓存大家的文书
  3. 控制是或不是拥有的资源是或不是要被缓存

JavaScript

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
      })
  );
});

地点的代码中,大家由此caches.open打开大家指定的cache文件名,然后我们调用cache.addAll并传到大家的文书数组。那是透过层层promise(caches.open

cache.addAll)达成的。event.waitUntil得到一个promise并接纳它来赢得安装费用的光阴以及是不是安装成功。

万一持有的公文都被缓存成功了,那么service
worker就安装成功了。若是其余一个文件下载败北,那么安装步骤就会败北。这些办法允许你依靠于您自己指定的兼具资源,但是那意味你必要越发严厉地控制哪些文件须求在设置步骤中被缓存。指定了太多的公文的话,就会大增设置失利率。

地点只是一个简练的例证,你能够在install事件中执行别的操作依旧甚至忽视install事件。

(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,固然跨域的资源协理CORS,那么可以把request的mod改成cors。倘诺请求战败了,如404如故是逾期等等的,那么也一向回到response让主页面处理,否则的话表达加载成功,把那么些response克隆一个置于cache里面,然后再回到response给主页面线程。注意能缓慢存里的资源一般只可以是GET,通过POST获取的是无法缓存的,所以要做个判断(当然你也得以手动把request对象的method改成get),还有把一些私家不希望缓存的资源也做个判断。

如此只要用户打开过四遍页面,ServiceWorker就设置好了,他刷新页面或者打开第一个页面的时候就能够把请求的资源一一做缓存,包罗图片、CSS、JS等,只要缓存里有了不管用户在线或者离线都能够健康访问。那样大家本来会有一个难点,这几个缓存空间到底有多大?上一篇大家提到Manifest也算是地点存储,PC端的Chrome是5Mb,其实那几个说法在新本子的Chrome已经不确切了,在Chrome
61版本能够看出地点存储的半空中和拔取情形:

必发88 17

中间Cache Storage是指ServiceWorker和Manifest占用的空中大小和,上图可以看出总的空间尺寸是20GB,大概是unlimited,所以基本上不用担心缓存会不够用。

哪些缓存和再次来到Request

你已经安装了service worker,你现在得以回来您缓存的呼吁了。

当service
worker被安装成功还要用户浏览了另一个页面或者刷新了当前的页面,service
worker将上马接受到fetch事件。上面是一个事例:

JavaScript

self.addEventListener(‘fetch’, function(event) { event.respondWith(
caches.match(event.request) .then(function(response) { // Cache hit –
return response if (response) { return response; } return
fetch(event.request); } ) ); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
self.addEventListener(‘fetch’, function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit – return response
        if (response) {
          return response;
        }
 
        return fetch(event.request);
      }
    )
  );
});

上边的代码里大家定义了fetch事件,在event.respondWith里,大家传入了一个由caches.match爆发的promise.caches.match
查找request中被service worker缓存命中的response。

假定大家有一个命中的response,大家回来被缓存的值,否则大家回去一个实时从互连网请求fetch的结果。那是一个极度简单的事例,使用具有在install步骤下被缓存的资源。

一经大家想要增量地缓存新的乞请,大家可以通过处理fetch请求的response并且拉长它们到缓存中来落到实处,例如:

JavaScript

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

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
31
32
33
34
35
36
37
38
39
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;
          }
        );
      })
    );
});

代码里大家所做工作包括:

  1. 加上一个callback到fetch请求的 .then 方法中
  2. 假定大家收获了一个response,咱们开展如下的反省:
    1. 有限支撑response是立见成效的
    2. 检查response的气象是或不是是200
    3. 保障response的系列是basic,那象征请求我是同源的,非同源(即跨域)的呼吁也不能被缓存。
  3. 借使我们通过了检讨,clone其一请求。这么做的原因是一旦response是一个Stream,那么它的body只好被读取五回,所以大家得将它克隆出来,一份发给浏览器,一份发给缓存。

(4)cache html

上面第(3)步把图片、js、css缓存起来了,然而假使把页面html也缓存了,例如把首页缓存了,就会有一个窘迫的题材——ServiceWorker是在页面注册的,可是现在到手页面的时候是从缓存取的,每回都是均等的,所以就造成力不从心立异ServiceWorker,如变成sw-5.js,不过PWA又须要大家能缓存页面html。那如何做吧?谷歌(谷歌)的开发者文档它只是提到会存在那些题材,但并没有证实怎么解决这一个标题。这一个的题材的解决就要求大家要有一个建制能精通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 URL(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.update提姆e) {
console.log(“update page html”); // 若是有立异则重复得到html
util.fetchPut(htmlRequest); pageUpdate提姆e[pageName] =
content.updateTime; } }); }); }, delCache: function (url) {
caches.open(CACHE_NAME).then(cache => { console.log(“delete cache “

  • url); cache.delete(url, {ignoreVary: true}); }); } };
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: []}}

其中主要有一个updateTime的字段,如若地点内存没有这么些页面的update提姆e的数码或者是和新型update提姆e分裂,则另行去获取
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 “

  • 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
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。用户发表完评论后会触发刷新页面,刷新的时候缓存已经被删了就会重新去伏乞了。

那般就一举成功了实时更新的题材。

什么样立异一个Service Worker

你的service
worker总有亟待立异的那一天。当那一天来到的时候,你要求按照如下步骤来更新:

  1. 更新您的service worker的JavaScript文件
    1. 当用户浏览你的网站,浏览器尝试在后台下载service
      worker的剧本文件。只要服务器上的文书和地点文件有一个字节不相同,它们就被判定为索要更新。
  2. 立异后的service worker将上马运作,install event被重复触发。
  3. 在那些时刻节点上,当前页面生效的仍旧是老版本的service
    worker,新的servicer worker将跻身”waiting”状态。
  4. 此时此刻页面被关门之后,老的service worker进度被杀死,新的servicer
    worker正式生效。
  5. 假定新的service worker生效,它的activate事件被触发。

代码更新后,日常要求在activate的callback中推行一个管制cache的操作。因为您会须要排除掉此前旧的多少。大家在activate而不是install的时候实施这么些操作是因为假如我们在install的时候马上执行它,那么依旧在运作的旧版本的数目就坏了。

前边大家只利用了一个缓存,叫做my-site-cache-v1,其实我们也可以使用多个缓存的,例如一个给页面使用,一个给blog的内容提交使用。这意味着,在install步骤里,我们可以创建两个缓存,pages-cache-v1和blog-posts-cache-v1,在activite步骤里,我们可以删除旧的my-site-cache-v1。

下边的代码可以循环所有的缓存,删除掉所有不在白名单中的缓存。

JavaScript

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
          }
        })
      );
    })
  );
});

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

要缓存可以使用二种手段,使用Http
Cache设置缓存时间,也足以用Manifest的Application Cache,还是可以用ServiceWorker缓存,如若三者都用上了会怎么啊?

会以Service Worker为先行,因为ServiceWorker把请求拦截了,它首先做拍卖,假设它缓存库里一些话平素回到,没有的话正常请求,就相当于尚未ServiceWorker了,这些时候就到了Manifest层,Manifest缓存里如果有些话就取那一个缓存,要是没有的话就一定于尚未Manifest了,于是就会从Http缓存里取了,若是Http缓存里也从未就会发请求去获得,服务端根据Http的etag或者Modified
提姆e可能会回去304 Not
Modified,否则正常重回200和数量内容。那就是整一个获取的进程。

就此假若既用了Manifest又用ServiceWorker的话应该会招致同一个资源存了四回。可是能够让帮忙ServiceWorker的浏览器选拔Service Worker,而不协助的施用Manifest.

拍卖边界和填坑

这一节内容相比较新,有不少待定细节。希望这一节很快就不需求讲了(因为标准会处理这个题材——译者注),不过现在,这个情节如故应当被提一下。

5. 施用Web App Manifest添加桌面入口

留神那里说的是别的一个Manifest,这一个Manifest是一个json文件,用来放网站icon名称等音讯以便在桌面添加一个图标,以及成立一种打开那些网页就好像打开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,从变化的图标打开就会像打开一个App一样,没有浏览器地址栏那个东西了。start_url指定打开未来的入口链接。

下一场添加一个link标签指向这些manifest文件:

必发88,JavaScript

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

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

那样组合Service Worker缓存:
必发88 18把start_url指向的页面用ServiceWorker缓存起来,那样当用户用Chrome浏览器打开那几个网页的时候,Chrome就会在尾部弹一个唤起,询问用户是或不是把那一个网页添加到桌面,固然点“添加”就会变卦一个桌面图标,从那么些图标点进去就好像打开一个App一样。感受如下:

必发88 19

正如为难的是Manifest近年来唯有Chrome协助,并且只可以在安卓系统上运用,IOS的浏览器无法添加一个桌面图标,因为IOS没有开放那种API,可是本人的Safari却又是足以的。

综上,本文介绍了怎么用Service Worker结合Manifest做一个PWA离线Web
APP,紧如果用ServiceWorker控制缓存,由于是写JS,比较灵活,还是可以与页面进行通信,此外通过请求页面的换代时间来判断是还是不是要求更新html缓存。ServiceWorker的包容性不是更加好,不过前景比较光明,浏览器都在预备协助。现阶段得以整合offline
cache的Manifest做离线应用。

连带阅读:

  1. 何以要把网站升级到HTTPS
  2. 何以把网站升级到http/2
  3. 我是何许让网站用上HTML5
    Manifest

1 赞 1 收藏
评论

必发88 20

假若设置失败了,没有很优雅的法子获得通报

一经一个worker被注册了,然而尚未出现在chrome://inspect/#service-workers或chrome://serviceworker-internals,那么很可能因为异常而安装失败了,或者是产生了一个被拒绝的的promise给event.waitUtil。

要解决那类难点,首先到 chrome://serviceworker-internals检查。打开开发者工具窗口准备调试,然后在你的install event代码中添加debugger;语句。这样,通过断点调试你更容易找到问题。

fetch()如今仅扶助Service Workers

fetch立刻匡助在页面上采纳了,但是当前的Chrome完毕,它还只协助service
worker。cache
API也就要在页面上被帮助,但是近日甘休,cache也还不得不在service
worker中用。

fetch()的默许参数

当你利用fetch,缺省地,请求不会带上cookies等凭证,要想带上的话,需求:

JavaScript

fetch(url, { credentials: ‘include’ })

1
2
3
fetch(url, {
  credentials: ‘include’
})

如此设计是有理由的,它比XHR的在同源下默许发送凭据,但跨域时抛弃凭据的平整要来得好。fetch的一言一动更像任何的CORS请求,例如<img crossorigin>,它默认不发送cookies,除非你指定了<img crossorigin="use-credentials">.。

Non-CORS默许不帮忙

默许情状下,从第三方URL跨域得到一个资源将会战败,除非对方支持了CORS。你可以加上一个non-CORS选项到Request去避免退步。代价是这么做会回来一个“不透明”的response,意味着你不能查出那个请求究竟是旗开得胜了依然败诉了。

JavaScript

cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) { return new
Request(urlToPrefetch, { mode: ‘no-cors’ }); })).then(function() {
console.log(‘All resources have been fetched and cached.’); });

1
2
3
4
5
cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
  return new Request(urlToPrefetch, { mode: ‘no-cors’ });
})).then(function() {
  console.log(‘All resources have been fetched and cached.’);
});

fetch()不遵从30x重定向规范

不幸,重定向在fetch()中不会被触发,那是当下版本的bug;

处理响应式图片

img的srcset属性或者<picture>标签会根据情况从浏览器或者网络上选择最合适尺寸的图片。

在service worker中,你想要在install步骤缓存一个图纸,你有以下二种选取:

  1. 安装具有的<picture>元素或者将被请求的srcset属性。
  2. 设置单一的low-res版本图片
  3. 设置单一的high-res版本图片

相比较好的方案是2或3,因为如若把持有的图样都给下载下来存着有点浪费内存。

假如你将low-res版本在install的时候缓存了,然后在页面加载的时候你想要尝试从互联网上下载high-res的本子,不过如果high-res版本下载失利以来,就依然用low-res版本。这一个想法很好也值得去做,可是有一个题材:

一旦我们有上边三种图片:

Screen Density Width Height
1x 400 400
2x 800 800

HTML代码如下:

JavaScript

<img src=”image-src.png” srcset=”image-src.png 1x, image-2x.png 2x”
/>

1
<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" />

要是大家在一个2x的显示形式下,浏览器会下载image-2x.png,要是大家离线,你能够读取从前缓存并回到image-src.png替代,假使从前它已经被缓存过。即使如此,由于现在的方式是2x,浏览器会把400X400的图纸彰显成200X200,要幸免那么些题材就要在图纸的体裁上设置宽高。

JavaScript

<img src=”image-src.png” srcset=”image-src.png 1x, image-2x.png 2x”
style=”width:400px; height: 400px;” />

1
2
<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x"
style="width:400px; height: 400px;" />

必发88 21

<picture>标签情况更复杂一些,难度取决于你是如何创建和使用的,但是可以通过与srcset类似的思路去解决。

改变URL Hash的Bug

在M40版本中留存一个bug,它会让页面在改动hash的时候造成service
worker甘休工作。

你可以在那里找到越来越多相关的信息: 

越多内容

那里有部分有关的文档能够参见:

获得扶持

只要您遇见麻烦,请在Stackoverflow上发帖询问,使用‘service-worker’标签,以便于大家马上跟进和不择手段帮忙你解决难题。

赞 2 收藏
评论

必发88 22

发表评论

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

网站地图xml地图