Menu

worker实现加速美高梅开户



使用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应用通常所不具备的富离线体验,定时的静默更新,消息通知推送等功能。而新的Service
workers标准让在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>
  • 复制代码

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

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

在你的sw.js中修改

JavaScript

const ignoreFetch = [ /https?:\/\/cdn.bootcss.com\//,
/https?:\/\/static.duoshuo.com\//,
/https?:\/\/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都引用了哪些第三方资源,逐个加到忽略列表里。

美高梅开户 1

在根目录下添加offline.html,在没有网络且缓存中也没有时使用,效果如下:

美高梅开户 2

在根目录下添加offline.svg,在无网络时图片资源请求返回该文件。

Service Worker 是什么?

一个 service worker
是一段运行在浏览器后台进程里的脚本,它独立于当前页面,提供了那些不需要与web页面交互的功能在网页背后悄悄执行的能力。在将来,基于它可以实现消息推送,静默更新以及地理围栏等服务,但是目前它首先要具备的功能是拦截和处理网络请求,包括可编程的响应缓存管理。

为什么说这个API是一个非常棒的API呢?因为它使得开发者可以支持非常好的离线体验,它给予开发者完全控制离线数据的能力。

在service worker提出之前,另外一个提供开发者离线体验的API叫做App
Cache。然而App
Cache有些局限性,例如它可以很容易地解决单页应用的问题,但是在多页应用上会很麻烦,而Service
workers的出现正是为了解决App Cache的痛点。

下面详细说一下service worker有哪些需要注意的地方:

  • 它是JavaScript
    Worker,所以它不能直接操作DOM。但是service
    worker可以通过postMessage与页面之间通信,把消息通知给页面,如果需要的话,让页面自己去操作DOM。
  • Service
    worker是一个可编程的网络代理,允许开发者控制页面上处理的网络请求。
  • 在不被使用的时候,它会自己终止,而当它再次被用到的时候,会被重新激活,所以你不能依赖于service
    worker的onfecth和onmessage的处理函数中的全局状态。如果你想要保存一些持久化的信息,你可以在service
    worker里使用IndexedDB API。
  • Service
    worker大量使用promise,所以如果你不了解什么是promise,那你需要先阅读这篇文章。

加速效果

首页加速后,网络请求从16降为1,加载时间从2.296s降为0.654s,得到了瞬间加载的结果。

美高梅开户 3

基于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初次安装的生命周期:

美高梅开户 4

加速/离线原理探索

在我们开始写码之前

从这个项目地址拿到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了解最佳实践。

什么是 Service worker

美高梅开户 5

如上图,Service
worker

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

可以被用来管理缓存,当响应一个网络请求时可以配置为返回缓存还是从网络获取。由于Service
worker

是基于事件的,所以它只在处理这些事件的时候被调入内存,不用担心常驻内存占用资源导致系统变慢。

使用Service Worker

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

Service worker生命周期

美高梅开户 6

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是否对你的网站启用了。

美高梅开户 7

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

你会发现这个功能能够很方便地在一个模拟窗口中测试你的service
worker,这样你可以关闭和重新打开它,而不会影响到你的新窗口。任何创建在模拟窗口中的注册服务和缓存在窗口被关闭时都将消失。

Service worker的控制从第二次页面访问开始

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

在首次加载时不会获取控制网络响应,它只会在后续访问页面时起作用。

美高梅开户 8

页面首次加载时完成install,并进入idle状态。

美高梅开户 9

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

美高梅开户 10

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

特点

  • 浏览器

Google 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所处的环境是相互隔离的。

美高梅开户 11

  • 没有浏览页面时也可以运行

接收系统事件,后台运行

  • 事件驱动,需要时运行,不需要时就终止

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

  • 可升级

执行时会异步获取最新的版本

怎样缓存和返回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只能被读取一次,所以我们得将它克隆出来,一份发给浏览器,一份发给缓存。

实现加速/离线

如何更新一个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);
          }
        })
      );
    })
  );
});

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;" />

美高梅开户 12

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

特定网站

  1. Google Chrome

Developer Tools->Application->Service Workers

美高梅开户 13

在这里还有三个非常有用的复选框:

  • Offline

模拟断网状态

  • Update on reload
    加载时更新
  • Bypass for network
    总是使用网络内容
  1. Firefox

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

美高梅开户 14

  1. Opera及其它双核浏览器同Google 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个Service
worker了,在这里可以手动Start让它工作,也可以Unregister卸载掉。

美高梅开户 15

  1. Firefox

有两种方式进入Service
worker
管理界面来手动Start或unregister。

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

JavaScript

about:debugging#workers

1
about:debugging#workers

美高梅开户 16

  1. Opera及其它双核浏览器同Google Chrome

更多内容

这里有一些相关的文档可以参考:

更多

TODO:

  • Service
    workers
    的更新需要手动编辑version,每次发布新文章时需要编辑。
  • 使用AMP让页面渲染速度达到最高。

获得帮助

如果你遇到麻烦,请在Stackoverflow上发帖询问,使用‘service-worker’标签,以便于我们及时跟进和尽可能帮助你解决问题。

赞 2 收藏
评论

美高梅开户 17

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 收藏
评论

美高梅开户 17

标签:,

发表评论

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

相关文章

网站地图xml地图