Service Worker初体验

yzc888,Service Worker初体验

2016/01/06 · JavaScript
· Service Worker

初稿出处: AlloyTeam   

在二〇一六年,W3C发布了service worker的草案,service
worker提供了过多新的力量,使得web app拥有与native
app相同的离线体验、音讯推送体验。
service worker是一段脚本,与web
worker一样,也是在后台运行。作为3个独立的线程,运行条件与平时脚本不一致,所以不能直接参预web交互行为。native
app可以成功离线使用、音信推送、后台自动更新,service
worker的产出是幸而为了使得web app也足以具有类似的力量。

 

service worker可以:

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

正文以财富缓存为例,说爱他美(Aptamil)下service worker是哪些做事的。

作用:

能够使您的运用先走访当地缓存能源,所以在离线状态时,在尚未经过互联网收到到越多的数量前,还能够提供基本的服从。

Service Worker是什么


从作用上来说,Service Worker是一种提供离线缓存控制功效的一种Worker,同时,也持有音讯推送和后台同步的功能,可以透过ServiceWorker来缓存网页的能源,然后拦截Fetch请求来实施相应的缓存处理操作。因为是一种Worker,所以ServiceWorker也颇具Worker的一部分为主特点,例如:

  • 独自于主线程运转
  • 无法访问window指标,可是富有自个儿的一个进行上下文,例如Service WorkerServiceWorkerGlobalScope
  • 装有新闻api来和页面进行新闻交互

ServiceWorker是一种共享型Worker,它分裂于专用型Worker只幸好开立它的页面中选拔,私下认可配置下,service
Worker能够被当下注册脚本的域名下具有页面公用。
也正是说,只要在一个根域名注册多个ServiceWorker,那么全部这么些域名下的页面都会接受影响。

简书放不了demo,demo能够看原文

生命周期

先来看一下叁个service worker的运营周期

yzc888 1
上海教室是service
worker生命周期,出处

图中得以见到,三个service worker要经历以下进程:

  1.  安装

2.
 激活,激活成功之后,打开chrome://inspect/#service-workers能够查看到当下运作的service
worker

yzc888 2

  1. 监听fetch和message事件,上面二种事件会开始展览简易描述

  2. 销毁,是还是不是销毁由浏览器决定,假诺一个service
    worker长期不利用恐怕机器内部存款和储蓄器有数,则或许会销毁这些worker

应用前的设置:

Chrome中须求敞开相关安插: 访问
chrome://flags 并打开
experimental-web-platform-features; 重启浏览器
(注意:有些性情在Chrome中尚无私下认可开放帮助);其它,你需求通过 HTTPS
来访问你的页面 — 出于安全原因,瑟维斯 Workers 要求要在必须在 HTTPS
下才能运行,localhost 也被浏览器认为是安全源。

Service Worker使用


要选择ServiceWorker,首先,供给通过serviceWorkerContainer.register()来展开注册,例如:

ServiceWorkerContainer.register("/test/service.js", {scope:"./"})
    .then(
        function(ServiceWorkerRegistration) {
            // do something
        }
);

假定当前域名为www.baidu.com/serviceWork,那么地点的主意就在www.baidu.com/serviceWork/test上边注册了一个ServiceWorker,假使把scope改成./hahaha,那么功能域就变成了www.baidu.com/serviceWork/test/hahaha,只要处于这几个域名之下的有着页面,都备受这几个瑟维斯Worker的支配。然而,假诺将scope改成”../”,页面就会报出三个错误,因为这时不允许设置比service.js所在地方层级更高的路径,除非添加2个Service-Worker-Allowedheader。

登记成功以往,受控界面会去安装serviceWorker,安装到位之后会处于等候景况,接下去有可能会进来激活状态,激活状态之后,页面还不必然是受控的,可是有照应的api能够控制这一类别流程。那一个流程就是ServiceWorker最最复杂的生命周期了。

Service Worker初体验。Service Worker 是什么?

service worker 是单身于当下页面包车型地铁一段运转在浏览器后台进度里的剧本。
service worker不必要用户打开 web
页面,也不须求其余交互,异步地运行在一个一心独立的上下文环境,不会对主线程造成堵塞。基于service
worker可以达成音讯推送,静默更新以及地理围栏等服务。
service
worker提供一种渐进增强的特点,使用性子检测来逐步增强,不会在老旧的不扶助service workers 的浏览器中生出震慑。可以通过service
workers消除让应用程序可以离线工作,让存款和储蓄数据在离线时使用的题材。

注意事项:
1.service
worker运作在它们自身的一点一滴独立异步的大局上下文中,约等于说它们有和好的器皿。
2.service
worker尚无一向操作DOM的权柄,不过足以通过postMessage方法来与Web页面通讯,让页面操作DOM。
3.service
worker是三个可编制程序的互连网代理,允许开发者控制页面上拍卖的网络请求。
4.浏览器大概随时回收service
worker,在不被运用的时候,它会融洽终止,而当它再也被用到的时候,会被再一次激活。
5.service worker的生命周期是由事件驱动的而不是经过Client。

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 fetch德姆o() { // fetch(url,
option)扶助多个参数,option中能够安装header、body、method信息fetch(U普拉多L).then(function(response) { // 通过promise
对象获得相应内容,并且将响应内容根据json格式转成对象,json()方法调用之后回到的仍旧是promise对象
// 也能够把内容转化成arraybuffer、blob对象 return response.json();
}).then(function(json) { // 渲染页面 insertPhotos(json); }); }
fetch德姆o();

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援救。

粗略的例子

那是把express和sw-test不难结合的3个小demo, 项目运作起来访问
http://localhost:3000/sw-test/index.html
,
然后终止此服务还能访问相应能源。Github

ServiceWorker生命周期


serviceWorker的生命周期有点复杂,情状多多,然而大多符合2个观点,这正是渐进式。

Service Worker生命周期

service worker拥有三个一心独立于Web页面包车型地铁生命周期

yzc888 3

sw-lifecycle.png

  1. 注册service worker,在网页上生效
  2. 设置成功,激活 只怕 安装失利(下次加载会尝试重新安装)
  3. 激活后,在sw的功用域下功效具有的页面,第1回决定sw不会生效,下次加载页面才会生效。
  4. sw功用页面后,处理fetch(互连网请求)和message(页面音讯)事件 只怕被终止(节本省部存款和储蓄器)。

message事件

页面和serviceWorker之间能够因而posetMessage()方法发送音讯,发送的消息能够通过message事件接收到。

那是3个双向的经过,页面能够发音讯给service worker,service
worker也足以发送新闻给页面,由于这么些特点,能够将service
worker作为中间纽带,使得八个域名照旧子域名下的五个页面能够随意通讯。

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

相关代码

  • /public/sw-test/app.js
  1. 第1判断了浏览器是或不是援救
  2. 调用 register 方法注册 service worker, 第三个参数是运作 service
    worker 的
    js 文件, 第三个 scope 参数是选填的,可以被用来内定你想让 service
    worker 控制的情节的子目录。 在那么些例子,大家钦赐了 ‘/sw-test/’,即
    http://localhost:3000/sw-test/
    下的伸手会被擒获, 被钦点的财富会被缓存。
  3. register 方法重临一个 Promise , 进行正确错误处理。

  if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' }).then(function(reg) {
    // registration worked
    console.log('Registration succeeded. Scope is ' + reg.scope);
  }).catch(function(error) {
    // registration failed
    console.log('Registration failed with ' + error);
  });
}
  • /public/sw-test/sw.js
    着力文件,监听安装事件, 打开缓存 v1 充实内需缓存财富 request url
    list, 截取被控文件下请求, 若是不设有该缓存则展开缓存处理
  1. 监听了 install 事件, event.waitUntil 首要用在 Install, activate
    事件中,
    在服务办事线程中,延长事件的寿命从而阻碍浏览器在事件中的异步操作完成在此之前终止服务办事线程。
  2. Cache 接口提供缓存的
    Request,
    Response
    对象对的蕴藏机制,例如作为ServiceWorker生命周期的一片段。
    Cache 接口像 workers 一样, 是暴光在 window
    功用域下的。即便它被定义在 service worker 的正统中,
    然则它不用一定要合营 service worker
    使用.Cache详细API
  3. event.respondWith
    方法意在包裹代码,那些代码为来自受控页面包车型地铁request生成自定义的response,查阅越多。response.clone()
    创立了一个响应对象的仿制,这一个目的在富有方面都以一律的,然则存款和储蓄在八个不比的变量中。幸免频仍行使篡改了对象。

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        '/sw-test/star-wars-logo.jpg',
        '/sw-test/gallery/bountyHunters.jpg',
        '/sw-test/gallery/myLittleVader.jpg',
        '/sw-test/gallery/snowTroopers.jpg'
      ]);
    })
  );
});

self.addEventListener('fetch', function(event) {
  event.respondWith(caches.match(event.request).then(function(response) {
    // caches.match() always resolves
    // but in case of success response will have value
    if (response !== undefined) {
      return response;
    } else {
      return fetch(event.request).then(function (response) {
        // response may be used only once
        // we need to save clone to put one copy in cache
        // and serve second one
        let responseClone = response.clone();

        caches.open('v1').then(function (cache) {
          cache.put(event.request, responseClone);
        });
        return response;
      }).catch(function () {
        return caches.match('/sw-test/gallery/myLittleVader.jpg');
      });
    }
  }));
});

install

推行完登记之后,浏览器会去下载,假若脚本没有不当的话,就会议及展览开安装,安装会在Worker内触发install事件,安装分为四个状态:installinginstalled,那里能够实施一些操作。

self.addEventListener("install",installEvent=>{
    self.skipWaiting();//跳过waiting
    installEvent.waitUntil(
        caches.open(CACHE_NAME).then(cache=>{
            return cache.add("http://upload.jianshu.io/users/upload_avatars/9545112/f186bd4a-1da4-4912-b926-a744bc128d06.png?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240");
        })
    )

});

那里会有一个install伊夫nt,通过waitUtil措施能够卡住安装状态,那些艺术能够流传叁个promise,唯有这几个promise正确resolve之后,才会成功安装,同时,还有2个skipwaiting方法,这几个法子能够跳过installed和activating状态之间的waiting状态。
第3遍打开页面会安装work,然后接下去再检测到新的work也会再次实施安装。
接触更新的三种景况:

  • 先是次导航到成效域范围内页面包车型客车时候
  • 当在24小时内并未开始展览立异检查和测试并且触发作用性时间如push或sync的时候
  • SW 的 UEscortL 发生变化并调用.register()时
  • 手动执行reg.update()
  • 重载页面(有个别情状下不会更新,原因不明)

履新会比对旧版本和新本子的字节,假使字节区别,则更新。在更新install实现未来,会进入waiting状态,直到旧版本不控制任何client(受控的页面)再进入activating状态。

Service Worker补助采纳

利用service workder缓存文件

下边介绍1个行使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代码。
依照后面包车型大巴生命周期图,在3个新的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的经过中,也际遇了有的题材,上边是中间八个

本子更新删除旧缓存

  1. 监听 activate 事件, 如当前版本 v2,删除与当前不匹配缓存数据。

this.addEventListener('activate', function(event) {
  var cacheWhitelist = ['v2'];

  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});

activate

activate的情状比测试妹子的怀想逻辑还难估摸。不过这一个遵照一个标准化,唯有全体的页面都不受老的serviceWorker控制的时候,才会起来激活。意思就是兼备worker功效的页面都关了,相当于重启更新,再度启航的时候,才会进入激活状态。激活也会接触贰个activate状态.

self.addEventListener("activate", ExtendableEvent => {
  ExtendableEvent.waitUntil(self.clients.claim());
 }

activate也和install一样,也有waitUtil措施,效果也是千篇一律的。激活成功未来实际还不自然有机能,因为页面恐怕还地处不受控的意况,恐怕有别的八个页面,没有刷新的,不过那几个时候新开3个页面更新了work,那些时候就供给调用clients.claim操作来让具有打开的页面受控。然则…work的施行是异步的,也正是说,页面在实施那几个主意以前是不受控,恐怕是还是不是受最新的work控制的,在动用work来缓存的时候,就会出现难点,导致版本不均等,所以,个人感觉serviceWorker比较相符做三个协助者,没有也行,有也行,能够经过设置3个button之类的来唤起用户,发现新本子,是或不是须要刷新之类的操作。
下边那张图很好的回顾了生命周期

yzc888 4

生命周期

浏览器帮忙

service worker
support

yzc888 5

navigator-serviceworker.png

网站地图xml地图