本文主要是介绍service workers是什么,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
什么是service workers,什么是service workers,如果你想知道什么是service workers,就让我来带你研究
认识service workers
什么是service workers
- Chrome官方在线demo
https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker - Service Worker简介
https://developers.google.com/web/fundamentals/primers/service-workers - Service Worker API - MDN
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API - W3C 官方标准
https://w3c.github.io/ServiceWorker/ - Service Worker相关的资源
https://jakearchibald.github.io
一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。
- 基于web worker(一个独立于JavaScript主线程的独立线程,在里面执行需要消耗大量资源的操作不会堵塞主线程)
- 在web worker的基础上增加了离线缓存的能力
- 本质上充当Web应用程序(服务器)与浏览器之间的代理服务器(可以拦截全站的请求,并作出相应的动作->由开发者指定的动作)
- 创建有效的离线体验(将一些不常更新的内容缓存在浏览器,提高访问体验)
- 由事件驱动的,具有生命周期
- 可以访问cache和indexDB
- 支持推送
- 并且可以让开发者自己控制管理缓存的内容以及版本
注意
Service worker运行在worker上下文 --> 不能访问DOM
它设计为完全异步,同步API(如XHR和localStorage)不能在service worker中使用
出于安全考量,Service workers只能由HTTPS承载
在Firefox浏览器的用户隐私模式,Service Worker不可用
其生命周期与页面无关(关联页面未关闭时,它也可以退出,没有关联页面时,它也可以启动)
一个简单的小demo
- 注册Service worker 在你的index.html加入以下内容
/* 判断当前浏览器是否支持serviceWorker */if ('serviceWorker' in navigator) {/* 当页面加载完成就创建一个serviceWorker */window.addEventListener('load', function () {/* 创建并指定对应的执行内容 *//* scope 参数是可选的,可以用来指定你想让 service worker 控制的内容的子目录。 在这个例子里,我们指定了 '/',表示 根网域下的所有内容。这也是默认值。 */navigator.serviceWorker.register('./serviceWorker.js', {scope: './'}).then(function (registration) {console.log('ServiceWorker registration successful with scope: ', registration.scope);}).catch(function (err) {console.log('ServiceWorker registration failed: ', err);});});}
- 安装worker:在我们指定的处理程序
serviceWorker.js
中书写对应的安装及拦截逻辑
/* 监听安装事件,install 事件一般是被用来设置你的浏览器的离线缓存逻辑 */
this.addEventListener('install', function (event) {/* 通过这个方法可以防止缓存未完成,就关闭serviceWorker */event.waitUntil(/* 创建一个名叫V1的缓存版本 */caches.open('v1').then(function (cache) {/* 指定要缓存的内容,地址为相对于跟域名的访问路径 */return cache.addAll(['./index.html']);}));
});/* 注册fetch事件,拦截全站的请求 */
this.addEventListener('fetch', function(event) {event.respondWith(// magic goes here/* 在缓存中匹配对应请求资源直接返回 */caches.match(event.request));
});
生命周期
Service Worker的初次运行生命周期流程如下图所示,从未注册开始主要分为Installed, Activated, IDLE 等阶段。
不同生命周期阶段转移的细节:
注册
在Service Worker被安装之前,首先需要在HTML页面内的脚本里注册server worker的脚本,即前面提到过的特定格式的JavaScript见(例如: sw.js)。注册这一步是在运行网页自身的脚本时告知浏览器:
这个页面是使用了Service Worker的,请加载指定路径的Service Worker的脚本并将它安装到当前域。
这一步本身不属于Service Worker的生命周期,因为它是需要在页面脚本里完成的,这和WebSocket比较类似。如下面的脚本(注意:它必须是HTML页面中script
标签的一部分,而不是sw.js中的):
if ('serviceWorker' in navigator) {navigator.serviceWorker.register('/sw.js').then(function(registration) {// Registration was successfulconsole.log(':) Success. ', registration.scope);}, function(err) {// registration failed :(console.log(':( Failed. ', err);});
}
上面的代码为当前页面所在的域注册了sw.js,我们还可以使用***scope***为它的指定更为细分的范围。也就是说,只有发送往当前域中来自scope中指定子路径的请求才会被Service Worker拦截,其他路径的请求将不受影响。如下图代码,只有向”https://domain/app/“及其子路径作为url的请求才会被service-worker.js
里绑定的事件响应。
navigator.serviceWorker.register('/service-worker.js', {scope: '/app/'
});
若scope未被指定,那么默认的scope是获取service worker脚本所在的同级域,及其响应的子域。
生命周期事件
Service Worker中的主要生命周期阶段的变化,是通过事件来通知脚本的,所以Serice Worker的脚本主要需要做的是为不同生命周期事件绑定好对应的处理器。核心的生命周期事件处理器如图示:
下载事件 (Dowload)
下载事件未在上图中标出,它发生在脚本注册阶段被触发且由浏览器处理: 1) 从指定路径下载service worker的脚本; 2) 完成脚本的解析,如果不符合service worker脚本的规范则不会触发安装事件; 3) 正确的脚本被安装到浏览器的Service Worker列表,被触发安装事件; 下载失败,解析失败或者初始化错误引发的错误也可以在浏览器的开发者工具 -> 应用 -> Service Worker列表看到,
安装事件 (Install)
在下载事件被浏览器响应完成后,安装事件是Service Worker生命周期中第一个被触发的,所以它非常重要。我们通常使用它来完成各种初始化任务,主要是资源的预加载、缓存以及持久化一些长期有效的状态。
这里主要被使用的是Cache API,监听到安装事件就可以预先拉取一些资源,并在应用缓存中存储它们并用给定的名字标识,参见下面的代码示例。在下文中有关于API的更详细的内容。
const CACHE_NAME = 'my-site-cache-v1';
const urlsToCache = [ // The list of resources to cache'/','/styles/main.css','/script/main.js'
];self.addEventListener('install', event => {// Perform install stepsevent.waitUntil(caches.open(CACHE_NAME).then(function(cache) {console.log('Opened cache');return cache.addAll(urlsToCache);}));
});
鉴于Service Worker本质上类似于Web Worker,它的资源加载完全独立于页面进程,所以它完全不会影响首屏渲染的性能。当然了,我们也完全不能预期在首次加载中就能受到应用缓存带来的优化。
激活事件 (activate)
当Service Worker已经就绪,可以控制页面请求并处理功能事件如fetch
,push
和sync
,激活事件就会被触发,我们监听激活事件的处理器就可以响应。
self.addEventListener('activate', event => {console.log('V1 now ready to handle fetches!');
});
激活事件标志着从这之后的请求将被Service Worker接管,所以通常用于区分Service Worker接管前后的分隔。 但这不意味着当前这次有效完成".register()"的页面会受到Service Worker 管理,因为我们无法预测页面资源先获取完成还是激活事件先响应。
请求事件 (fetch)
fetch
事件被触发于页面对网络发起任意请求,当然我们前面已经提过:1)必须在激活事件触发后service worker接管网络请求;2)请求的目标url必须位于注册的资源范围(scope)内。
来看一下这个 demo, 这里Service Worker劫持了"fetch"请求,但只在激活后才会生效:
- 当你第一次打开这个demo页面,你应该在等待后看到一张狗的图片,因为这个页面的标签指向的就是一张dog.svg。对这个图片的请求在完成Service Worker的注册之前就已经发出去了,响应的自然是狗的图片。
- 然后刷新页面,这一次我们看到的却是猫的剪影图片了,之后反复刷新都会是猫的图片。因为Service Worker已经完成了注册,开始接管对图片资源的HTTP请求,并将我们的请求换成它准备好的猫图了。
我们来看看SW的脚本里都做了什么:
// Caching the cat image when installing
self.addEventListener('install', event => {console.log('V2 installing…');// cache a horse SVG into a new cache, static-v2event.waitUntil(caches.open('static-v2').then(cache => cache.add('/cat.svg')));
});// Response the cat image when requesting dog.
self.addEventListener('fetch', event => {const url = new URL(event.request.url); // serve the cat SVG from the cache if the request is// same-origin and the path is '/dog.svg'if (url.origin == location.origin && url.pathname.endsWith('/dog.svg')) {event.respondWith(caches.match('cat.svg'));}
});
- 在安装事件中,service worker请求了猫图cat.svg并加入缓存,这是页面进程完全不知道的。
- 而在fetch事件中,我们劫持了网络请求,当资源目标为狗图时,service worker没有乖乖地转发请求,而是从缓存列表取出猫图,回复给了页面。同样,我们的页面并不知道这个请求根本没有通过网络,而是被service worker返回的资源应付了。就会有这张猫图在页面的资源列表里被标注成了请求的dog.svg的奇妙效果:
在上面提到的Service Worker的开发这界面,我们可以通过点击"unregiter"来取消当前service worker脚本的注册,这样在下一次打开时虽然service worker会被再次注册,至少在那一次的页面我们见到的将还是狗图了。
这篇关于service workers是什么的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!