【必发88】连不上网,worker已毕加快

by admin on 2019年1月31日

利用Service worker完结加快/离线访问静态blog网站

2017/02/19 · JavaScript
· Service Worker

初稿出处: Yang
Bo   

前几天很盛行基于Github
page和markdown的静态blog,非凡适合技术的思索和习惯,针对差距的言语都有一对精美的静态blog系统出现,如Jekyll/Ruby,Pelican/Python,Hexo/NodeJs,由于静态内容的特色至极适合做缓存来增速页面的拜访,就利用Service
worker
来完成加快,结果是除了PageSpeed,CDN那几个常见的服务器和网络加速之外,通过客户端完毕了更好的造访体验。

Service Worker入门

2015/03/26 · JavaScript
· Service Worker

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

  • 十年踪迹]()   

原生App拥有Web应用一般所不富有的富离线体验,定时的沉默更新,音讯布告推送等功能。而新的瑟维斯workers标准让在Web App上保有这几个职能成为可能。

使用 Service worker 创设一个卓殊简单的离线页面

2016/06/07 · JavaScript
· 1 评论 · Service
Worker

本文由 伯乐在线 –
刘健超-J.c
翻译,艾凌风
校稿。未经许可,禁止转发!
英文出处:Dean
Hume。欢迎出席翻译组。

让我们想像以下场景:大家那儿在一辆通往农村的火车上,用移动设备望着一篇很棒的稿子。与此同时,当您点击“查看越多”的链接时,高铁忽然进入了隧道,导致运动装备失去了网络,而
web 页面会突显出类似以下的始末:

必发88 1

那是一定令人失落的体会!幸运的是,web
开发者们能透过有些新特色来革新那类的用户体验。我近年径直在折腾 ServiceWorkers,它给 web 带来的无尽可能性总能给自己惊喜。瑟维斯 Workers
的名特优特质之一是允许你检测互连网请求的处境,并让您作出相应的响应。

在那篇小说里,我打算用此特性检查用户的眼前互连网连接处境,如果没连接则赶回一个极品简单的离线页面。就算那是一个非凡基础的案例,但它能给你带来启发,让您掌握启动并运行该特性是多么的简便!假如你没明白过
Service Worker,我提出您看看此 Github
repo,精通越多相关的音讯。

在该案例开首前,让我们先简单地看望它的办事流程:

  1. 在用户首次访问大家的页面时,我们会设置 ServiceWorker,并向浏览器的缓存添加大家的离线 HTML 页面
  2. 下一场,要是用户打算导航到另一个 web
    页面(同一个网站下),但此时已断网,那么我们将赶回已被缓存的离线
    HTML 页面
  3. 可是,如果用户打算导航到其它一个 web
    页面,而此刻互连网已延续,则能照常浏览页面

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

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

本文由 伯乐在线 –
Erucy
翻译,weavewillg
校稿。未经许可,禁止转发!
英文出处:Oliver
Ash。欢迎出席翻译组。

大家是怎么样行使 service worker 来为 theguardian.com
打造一个自定义的离线页面。

必发88 2

theguardian.com 的离线页面。插图:Oliver Ash

您正在朝着集团途中的大巴里,在手机上开辟了
Guardian
应用。大巴被隧道包围着,然而这么些动用可以正常运行,即使没有网络连接,你也能得到完整的成效,除了体现的始末恐怕有点旧。借使您品尝在网站上也这样干,可惜它完全没法加载:

必发88 3

安卓版 Chrome 的离线页面

Chrome 中的这几个彩蛋,很几个人都不明了》

Chrome
在离线页面上有个暗藏的玩乐(桌面版上按空格键,手机版上点击那只恐龙),这有点能减轻一点你的烦心。然则大家得以做得更好。

Service
workers
允许网站作者拦截自己站点的有着网络请求,那也就象征我们可以提供周全的离线体验,似乎原生应用相同。在
Guardian
网站,大家目前上线了一个自定义的离线体验效果。当用户离线的时候,他们会看出一个分包
Guardian
标识的页面,下边带有一个简便的离线提醒,还有一个填字游戏,他们得以在守候互联网连接的时候玩玩这些找点乐子。那篇博客解释了俺们是什么样创设它的,然而在起来之前,你可以先自己摸索看。

加紧/离线访问只需三步

  • 首页添加注册代码

JavaScript

<script> if (‘serviceWorker’ in navigator) {
navigator.serviceWorker.register(‘/sw.js’); } </script>

1
2
3
4
5
<script>
if (‘serviceWorker’ in navigator) {
navigator.serviceWorker.register(‘/sw.js’);
}
</script>
  • 复制代码

将保存到您的网站根目录下

  • 修改不缓存域名列表及离线状态页面

在你的sw.js中修改

JavaScript

const ignoreFetch = [ /https?:\/\/cdn.bootcss.com\//,
/https?:\/\/static.duoshuo.com\//,
/https?:\【必发88】连不上网,worker已毕加快。/\/www.google-analytics.com\//,
/https?:\/\/dn-lbstatics.qbox.me\//, ];

1
2
3
4
5
6
const ignoreFetch = [
  /https?:\/\/cdn.bootcss.com\//,
  /https?:\/\/static.duoshuo.com\//,
  /https?:\/\/www.google-analytics.com\//,
  /https?:\/\/dn-lbstatics.qbox.me\//,
];

打开Chrome Dev Tools->Source,看看自己的blog都引用了什么样第三方资源,逐个加到忽略列表里。

必发88 4

在根目录下添加offline.html,在未曾网络且缓存中也绝非时接纳,效果如下:

必发88 5

在根目录下添加offline.svg,在无互连网时图片资源请求再次来到该公文。

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。
  • 瑟维斯worker大批量使用promise,所以若是你不打听怎么是promise,那您须求先读书这篇文章。

让大家初叶吧

比方你有以下 HTML 页面。那虽然那些基础,但能给您完全思路。

XHTML

<!DOCTYPE html>

1
<!DOCTYPE html>

紧接着,让大家在页面里登记 Service Worker,那里仅创设了该对象。向刚刚的
HTML 里添加以下代码。

JavaScript

<script> // Register the service worker // 注册 service worker if
(‘serviceWorker’ in navigator) {
navigator.serviceWorker.register(‘/service-worker.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); }); }
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
// Register the service worker
// 注册 service worker
if (‘serviceWorker’ in navigator) {
    navigator.serviceWorker.register(‘/service-worker.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);
   });
}
</script>

接下来,大家要求创立 Service Worker 文件并将其取名为
‘service-worker.js‘。大家打算用那么些 Service Worker
拦截任何互连网请求,以此检查网络的连接性,并根据检查结果向用户重回最契合的始末。

JavaScript

‘use strict’; var cacheVersion = 1; var currentCache = { offline:
‘offline-cache’ + cacheVersion }; const offlineUrl =
‘offline-page.html’; this.addEventListener(‘install’, event => {
event.waitUntil( caches.open(currentCache.offline).then(function(cache)
{ return cache.addAll([ ‘./img/offline.svg’, offlineUrl ]); }) ); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
‘use strict’;
 
var cacheVersion = 1;
var currentCache = {
  offline: ‘offline-cache’ + cacheVersion
};
const offlineUrl = ‘offline-page.html’;
 
this.addEventListener(‘install’, event => {
  event.waitUntil(
    caches.open(currentCache.offline).then(function(cache) {
      return cache.addAll([
          ‘./img/offline.svg’,
          offlineUrl
      ]);
    })
  );
});

在上边的代码中,大家在装置 瑟维斯 Worker
时,向缓存添加了离线页面。如若大家将代码分为几小块,可知到前几行代码中,我为离线页面指定了缓存版本和URL。即使您的缓存有例外版本,那么你只需立异版本号即可简单地清除缓存。在大致在第
12
行代码,我向那一个离线页面及其资源(如:图片)发出请求。在收获成功的响应后,大家将离线页面和相关资源丰裕到缓存。

前天,离线页面已存进缓存了,大家可在需求的时候检索它。在同一个 ServiceWorker 中,大家要求对无网络时再次来到的离线页面添加相应的逻辑代码。

JavaScript

this.add伊夫ntListener(‘fetch’, event => { // request.mode = navigate
isn’t supported in all browsers // request.mode = naivgate
并不曾拿走所有浏览器的支撑 // so include a check for Accept: text/html
header. // 因而对 header 的 Accept:text/html 进行核准 if
(event.request.mode === ‘navigate’ || (event.request.method === ‘GET’ &&
event.request.headers.get(‘accept’).includes(‘text/html’))) {
event.respondWith( fetch(event.request.url).catch(error => { //
Return the offline page // 再次回到离线页面 return caches.match(offlineUrl);
}) ); } else{ // Respond with everything else if we can //
重返任何我们能回到的东西 event.respondWith(caches.match(event.request)
.then(function (response) { return response || fetch(event.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
this.addEventListener(‘fetch’, event => {
  // request.mode = navigate isn’t supported in all browsers
  // request.mode = naivgate 并没有得到所有浏览器的支持
  // so include a check for Accept: text/html header.
  // 因此对 header 的 Accept:text/html 进行核实
  if (event.request.mode === ‘navigate’ || (event.request.method === ‘GET’ && event.request.headers.get(‘accept’).includes(‘text/html’))) {
        event.respondWith(
          fetch(event.request.url).catch(error => {
              // Return the offline page
              // 返回离线页面
              return caches.match(offlineUrl);
          })
    );
  }
  else{
        // Respond with everything else if we can
        // 返回任何我们能返回的东西
        event.respondWith(caches.match(event.request)
                        .then(function (response) {
                        return response || fetch(event.request);
                    })
            );
      }
});

为了测试该功效,你可以选取 Chrome
内置的开发者工具。首先,导航到你的页面,然后若是设置上了 ServiceWorker,就打开 Network 标签并将节流(throttling)改为
Offline。(译者注:若将节流设置为 Offline
没效果,则可透过关闭互联网或者经过360康宁警卫禁止 Chrome 访问网络)

必发88 6

比方你刷新页面,你应当能收六柱预测应的离线页面!

必发88 7

一经你只想大致地测试该成效而不想写任何代码,那么您可以访问我已创制好的
demo。其余,上述所有代码可以在
Github repo 找到。

自我清楚用在此案例中的页面很简短,但您的离线页面则在于你协调!倘使你想深切该案例的内容,你能够为离线页面添加缓存破坏(
cache busting),如:
此案例。

试试看

你要求一个支撑 Service
Worker 和 fetch
API 的浏览器。停止到本文编写时唯有Chrome(手机版和桌面版)同时襄助那二种 API(译者注:Opera
近年来也支撑那两者),可是 Firefox
很快就要接济了(在每天更新的本子中早就支撑了),除去 Safari
之外的保有浏览器也都在试行。其余,service worker 只好登记在运用了
HTTPS 的网站上,theguardian.com
已经上马逐步搬迁到 HTTPS,所以我们不得不在网站的 HTTPS
部分提供离线体验。就当前的话,大家选拔了 开发者博客 作为我们用来测试的地方。所以一旦您是在我们网站的 开发者博客 部分阅读那篇小说的话,很幸运。

当您使用援助的浏览器访问大家的 开发者博客 中的页面的时候,一切就准备妥当了。断开你的互连网连接,然后刷新一下页面。若是您协调没规范尝试的话,可以看一下这段 以身作则摄像(译者注:需梯子)。

加速效果

首页加速后,互联网请求从16降为1,加载时间从2.296s降为0.654s,获得了眨眼之间间加载的结果。

必发88 8

【必发88】连不上网,worker已毕加快。基于webpagetest

查看测试结果

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 9

开展阅读

此外,还有几个很棒的离线作用案例。如:Guardian 营造了一个具备 crossword
puzzle(填字游戏)的离线
web 页面 –
因而,尽管等待互联网重连时(即已在离线状态下),也能找到一点乐趣。我也援引看看
Google Chrome Github
repo,它涵盖了不计其数例外的
Service Worker 案例 – 其中一些运用案例也在那!

不过,假如您想跳过上述代码,只是想大致地通过一个库来处理有关操作,那么我引进你看看
UpUp。那是一个轻量的台本,能让你更轻松地利用离线效能。

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

打赏译者

做事规律

由此一段简单的
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的文章。

打赏援救自己翻译更加多好小说,谢谢!

打赏译者

加速/离线原理探索

在大家初始写码从前

从这个品类地址拿到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精通最佳实践。

打赏支持我翻译越多好文章,谢谢!

任选一种支付形式

必发88 10
必发88 11

1 赞 3 收藏 1
评论

打赏接济我翻译越多好小说,谢谢!

必发88 12

1 赞 收藏
评论

什么是 Service worker

必发88 13

如上图,Service
worker

是一种由Javascript编写的浏览器端代理脚本,位于你的浏览器和服务器之间。当一个页面注册了一个
Service
worker
,它就可以挂号一密密麻麻事件处理器来响应如网络请求和音信推送那些事件。Service
worker

可以被用来管理缓存,当响应一个网络请求时方可配备为回去缓存如故从互连网获取。由于Service
worker

是按照事件的,所以它只在处理这个事件的时候被调入内存,不用顾虑常驻内存占用资源导致系统变慢。

使用Service Worker

当今我们有了polyfill,并且搞定了HTTPS,让大家看看到底怎么用service
worker。

关于小编:刘健超-J.c

必发88 14

前端,在路上…
个人主页 ·
我的小说 ·
19 ·
    

必发88 15

有关作者:Erucy

必发88 16

业已的SharePoint喵星程序猿(暂时还挂着微软MVP的名头),现在的Azure/.Net/MongoDB/Cordova/前端程序猿,偶尔写小说
个人主页 ·
我的文章 ·
46 ·
  

必发88 17

瑟维斯 worker生命周期

必发88 18

必发88,Service
worker

为网页添加一个类似于APP的生命周期,它只会响应系统事件,即使浏览器关闭时操作系统也得以提醒Service
worker
,这一点万分重要,让web
app与native app的能力变得好像了。

Service
worker
在Register时会触发Install事件,在Install时方可用来预先获取和缓存应用所需的资源并设置每个文件的缓存策略。

一旦Service
worker
处于activated状态,就足以完全控制应用的资源,对互联网请求进行检讨,修改网络请求,从网络上赢得并再次来到内容可能再次回到由已安装的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 19

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

您会发现这些意义可以很有益地在一个模拟窗口中测试你的service
worker,那样您可以关闭和另行打开它,而不会影响到你的新窗口。任何创建在模拟窗口中的注册服务和缓存在窗口被关闭时都将消失。

瑟维斯 worker的支配从第二次页面访问初步

在首次加载页面时,所有资源都是从互连网载的,Service
worker

在第一次加载时不会收获控制网络响应,它只会在后续访问页面时起功效。

必发88 20

页面第一次加载时成功install,并进入idle状态。

必发88 21

页面第二次加载时,进入activated状态,准备处理所有的风云,同时
浏览器会向服务器发送一个异步 请求来检查Service
worker
自我是或不是有新的版本,构成了Service
worker
的翻新机制。

必发88 22

Service
worker
拍卖完所有的轩然大波后,进入idle状态,最终进入terminated状态资源被放出,当有新的风浪爆发时再一次被调用。

Service 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事件。

特点

  • 浏览器

谷歌 Chrome,Firefox,Opera以及国内的各类双核浏览器都帮衬,可是 safari
不帮衬,那么在不支持的浏览器里Service
worker
不工作。

  • https

网站必须启用https来保管使用Service
worker
页面的安全性,开发时localhost默许认为是平安的。

  • non-block

Service
worker

中的 Javascript 代码必须是非阻塞的,因为 localStorage
是阻塞性,所以不应有在 Service Worker 代码中选择 localStorage。

  • 独自的施行环境

Service
worker
运转在团结的大局环境中,平常也运行在自己独自的线程中。

  • 并未绑定到特定页面

service work能决定它所加载的全部范围内的资源。

  • 不可能操作DOM

跟DOM所处的环境是并行隔离的。

必发88 23

  • 从未浏览页面时也足以运行

收取系统事件,后台运行

  • 事件驱动,必要时运行,不须要时就甘休

按需举行,只在必要时加载到内存

  • 可升级

施行时会异步获取最新的本子

怎么样缓存和再次来到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只好被读取几遍,所以大家得将它克隆出来,一份发给浏览器,一份发给缓存。

已毕加速/离线

什么样革新一个瑟维斯 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);
          }
        })
      );
    })
  );
});

Cache

网页缓存有好多,如HTTP缓存,localStorage,sessionStorage和cacheStorage都足以灵活搭配举办缓存,但操作太繁琐,直接行使更高级Service
worker

–本文的东家。

拍卖边界和填坑

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

添加Service worker入口

在web app的首页添加以下代码

JavaScript

<script> if (‘serviceWorker’ in navigator) {
navigator.serviceWorker.register(‘/sw.js’); } </script>

1
2
3
4
5
<script>
if (‘serviceWorker’ in navigator) {
navigator.serviceWorker.register(‘/sw.js’);
}
</script>

只要浏览器匡助serviceWorker就登记它,不支持依然健康浏览,没有Service
worker
所提供的增高功效。

Service worker控制范围:
粗略境况下,将sw.js位于网站的根目录下,那样Service
worker
可以决定网站有着的页面,,同理,假如把sw.js放在/my-app/sw.js那就是说它不得不控制my-app目录下的页面。
sw.js放在/js/目录呢?更好的目录结构和限量控制呢?
在注册时指定js地方并安装限制。

JavaScript

navigator.serviceWorker.register(‘/js/sw.js’, {scope:
‘/sw-test/’}).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
navigator.serviceWorker.register(‘/js/sw.js’, {scope: ‘/sw-test/’}).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);
    });

要是设置败北了,没有很优雅的法子获取通报

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

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

Service worker实现

监听五个事件:

JavaScript

self.addEventListener(‘install’, onInstall);
self.addEventListener(‘fetch’, onFetch);
self.addEventListener(“activate”, onActivate);

1
2
3
self.addEventListener(‘install’, onInstall);
self.addEventListener(‘fetch’, onFetch);
self.addEventListener("activate", onActivate);

fetch()近日仅帮衬Service Workers

fetch即刻匡助在页面上行使了,不过当前的Chrome完毕,它还只帮助service
worker。cache
API也即将在页面上被支持,但是近来为止,cache也还不得不在service
worker中用。

install

JavaScript

////////// // Install ////////// function onInstall(event) {
log(‘install event in progress.’); event.waitUntil(updateStaticCache());
} function updateStaticCache() { return caches
.open(cacheKey(‘offline’)) .then((cache) => { return
cache.addAll(offlineResources); }) .then(() => { log(‘installation
complete!’); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//////////
// Install
//////////
function onInstall(event) {
  log(‘install event in progress.’);
  event.waitUntil(updateStaticCache());
}
function updateStaticCache() {
  return caches
    .open(cacheKey(‘offline’))
    .then((cache) => {
      return cache.addAll(offlineResources);
    })
    .then(() => {
      log(‘installation complete!’);
    });
}

install时将持有符合缓存策略的资源举行缓存。

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">.。

fetch

JavaScript

//////// // Fetch //////// function onFetch(event) { const request =
event.request; if (shouldAlwaysFetch(request)) {
event.respondWith(networkedOrOffline(request)); return; } if
(shouldFetchAndCache(request)) {
event.respondWith(networkedOrCached(request)); return; }
event.respondWith(cachedOrNetworked(request)); }
onFetch做为浏览器网络请求的代理,根据要求再次来到互连网或缓存内容,假若得到了互联网内容,重临网络请求时同时展开缓存操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
////////
// Fetch
////////
function onFetch(event) {
  const request = event.request;
  if (shouldAlwaysFetch(request)) {
    event.respondWith(networkedOrOffline(request));
    return;
  }
  if (shouldFetchAndCache(request)) {
    event.respondWith(networkedOrCached(request));
    return;
  }
  event.respondWith(cachedOrNetworked(request));
}
onFetch做为浏览器网络请求的代理,根据需要返回网络或缓存内容,如果获取了网络内容,返回网络请求时同时进行缓存操作。

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

activate

JavaScript

/////////// // Activate /////////// function onActivate(event) {
log(‘activate event in progress.’); event.waitUntil(removeOldCache()); }
function removeOldCache() { return caches .keys() .then((keys) => {
return Promise.all( // We return a promise that settles when all
outdated caches are deleted. keys .filter((key) => { return
!key.startsWith(version); // Filter by keys that don’t start with the
latest version prefix. }) .map((key) => { return caches.delete(key);
// Return a promise that’s fulfilled when each outdated cache is
deleted. }) ); }) .then(() => { log(‘removeOldCache completed.’); });
}

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
///////////
// Activate
///////////
function onActivate(event) {
  log(‘activate event in progress.’);
  event.waitUntil(removeOldCache());
}
function removeOldCache() {
  return caches
    .keys()
    .then((keys) => {
      return Promise.all( // We return a promise that settles when all outdated caches are deleted.
        keys
         .filter((key) => {
           return !key.startsWith(version); // Filter by keys that don’t start with the latest version prefix.
         })
         .map((key) => {
           return caches.delete(key); // Return a promise that’s fulfilled when each outdated cache is deleted.
         })
      );
    })
    .then(() => {
      log(‘removeOldCache completed.’);
    });
}

在activate时依照version值来删除过期的缓存。

fetch()不依照30x重定向规范

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

管理 Service worker

处理响应式图片

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 24

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

一定网站

  1. Google Chrome

Developer Tools->Application->Service Workers

必发88 25

在那边还有三个越发有效的复选框:

  • Offline

效仿断网状态

  • Update on reload
    加载时更新
  • Bypass for network
    连日利用互联网内容
  1. Firefox

除非在Settings里有一个足以在HTTP环境中选用Service
worker
的选项,适应于调试,没有单独网站下的Service
worker
管理。

必发88 26

  1. Opera及别的双核浏览器同谷歌 Chrome
    万一看到多少个一样范围内的八个Service
    worker
    ,说明Service
    woker
    更新后,而原有Service
    worker
    还从未被terminated。

改变URL Hash的Bug

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

您可以在此间找到更加多相关的新闻: 

浏览器全局

探访你的浏览器里都有哪些Service worker已经存在了

  1. Google Chrome

在地址栏里输入:

JavaScript

chrome://serviceworker-internals/

1
chrome://serviceworker-internals/

可以看看曾经有24个Serviceworker了,在此间可以手动Start让它工作,也足以Unregister卸载掉。

必发88 27

  1. Firefox

有二种艺术进入Service
worker
管理界面来手动Start或unregister。

  • 菜单栏,Tool->Web Developer->Service workers
  • 地点栏中输入

JavaScript

about:debugging#workers

1
about:debugging#workers

必发88 28

  1. Opera及其他双核浏览器同谷歌 Chrome

越来越多内容

这里有部分巢倾卵破的文档可以参考:

更多

TODO:

  • Service
    workers
    的立异必要手动编辑version,每一次公布新小说时索要编制。
  • 使用AMP让页面渲染速度高达最高。

赢得扶持

若是你赶上麻烦,请在Stackoverflow上发帖询问,使用‘service-worker’标签,以便于大家当下跟进和不择手段扶助您解决难题。

赞 2 收藏
评论

必发88 29

Ref links

Service Worker Cookbook

Is service worker
ready?

Chrome service worker status
page

Firefox service worker status
page

MS Edge service worker status
page

WebKit service worker status
page

1 赞 2 收藏
评论

必发88 30

发表评论

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

网站地图xml地图