Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service Worker 从入门的实践 #54

Open
MengZhaoFly opened this issue Dec 27, 2019 · 0 comments
Open

Service Worker 从入门的实践 #54

MengZhaoFly opened this issue Dec 27, 2019 · 0 comments

Comments

@MengZhaoFly
Copy link
Owner

MengZhaoFly commented Dec 27, 2019

Web Worker

浏览器中的 javaScript 都是运行在一个单一主线程上的,在同一时间内只能做一件事情。
Web Worker 是 HTML5 标准的一部分,这一规范定义了一套 API,它允许一段 JavaScript 程序运行在主线程之外的另外一个线程中。

w3c-Web-Worker

Web Worker Service Worker 有什么关系呢,
Web Worker 是临时的,每次做的事情的结果还不能被持久存下来。
Service Worker 在 Web Worker 的基础上加上了持久离线缓存能力。

Service Worker 缓存 和 http 缓存

  • http 缓存

对于 cache-control, Etag, last-modified 等字段我们耳熟能详,这里不过多解释,对于这种缓存还是有缺点的:

  1. 断网的时候,都不能访问
  2. 缓存不能编程,不能精细地对需要缓存的内容增删改查
  • Service Worker
  1. 基于 Web Worker ,一个独立的 worker 线程,独立于当前网页进程;
  2. 让缓存做到优雅和极致

和 PWA 又有什么关系

PWA是为了解决传统Web APP的缺点:

  • 没有桌面入口
  • 无法离线使用
  • 没有Push推送

PWA是前端最火热的一个概念之一,Service Worker为PWA赋能离线可用性以及push消息:

  • 和 CacheStorage 搭配,可以做离线应用
  • 和 Notification 搭配,可以做像 Native APP 那样的消息推送
  • 再加上 Manifest 等,就差不多成了 PWA 了

MDN-CacheStorage-api

Service Worker 怎么用

业务需求:假设我当前页有:

  • 首页 html
  • 一个 get请求
  • 若干 js img 请求
[
	'./',
	'getList',
	'img/avatar_v1.jpg',
	'js/bundle.js',
]

下文将实现对它们的缓存。

简介

Service Worker 可能拥有以下六种状态的一种:

  • 解析成功(parsed)
  • 正在安装(installing)
  • 安装成功(installed)
  • 正在激活(activating)
  • 激活成功(activated)
  • 废弃(redundant)

image

Parsed

首次注册 Service Worker 时,如果满足 Service Worker 运行的条件(在 https / localhost)环境下。
这也是 第一步:

if ('serviceWorker' in navigator) {
	navigator.serviceWorker.register('./sw.js', { scope: './' })
		.then(function (reg) {
			console.log('注册 sw.js 完成');
		})
		.catch(function (err) {
			console.error('注册 sw.js 出错');
		})
}

Installing

Service Worker 脚本解析完成后,浏览器会试着安装,进入下一状态,“installing”

this.addEventListener('install', function (event) {
	console.log('安装 sw.js');
	event.waitUntil(
		caches.open(CACHE_NAME).then(function (cache) {
			return cache.addAll(
				[
					'./',
					'getList',
					'img/avatar_v1.jpg',
					'js/bundle.js',
				]
			);
		})
	)
});

我们通常在该环节缓存文件,
event.waitUntil() 方法,则 installing 事件会一直等到该方法中的 Promise 完成之后才会成功;若 Promise 被拒,则安装失败,Service Worker 直接进入废弃(redundant)状态。

激活(Activating)

第一次注册并安装成功后,会触发 activate 事件:

addEventListener('activate', event => {
  console.log('安装成功,监听作用域下的所有页面')
})

waiting

上面我们介绍的是第一次注册安装完毕,
但是我们缓存的内容不是一成不变的。不可能永远是

[
	'./',
	'getList',
	'img/avatar_v1.jpg',
	'js/bundle.js',
]

当有sw脚本更新时,在后台默默注册安装新的脚本文件,安装成功后进入 waiting 状态。

image

这个时候有两种选择:

  • 当前所有老版本控制的页面关闭后,再次打开时,新版本的脚本触发 activate 事件
  • 手动调用 this.skipWaiting();

基于这一点。
在 activate 我们会处理前后 sw脚本 需要管理的缓存,这里依据当前 sw.js 的缓存名字,判断实现清除之前的缓存。
为什么要处理:

  • 除非明确地更新缓存,否则缓存将不会被更新;除非删除,否则缓存数据不会过期
  • 浏览器都硬性限制了一个域下缓存数据的大小
this.addEventListener('activate', function (event) {
	console.log('激活 sw.js,可以开始处理 fetch 请求。');
	event.waitUntil(
		caches.keys().then(function (keyList) {
			return Promise.all(keyList.map(function (key) {
				console.log(CACHE_NAME, key)
				if (CACHE_NAME.indexOf(key) === -1) {
					/**
					 * delete() 方法查询request为key的 Cache 条目,如果找到,
					 * 则删除该 Cache 条目
					 */
					return caches.delete(key);
				}
			}))
		})
	)
});

Activated

如果 Service Worker 处于激活态,就可以应对事件性事件 —— fetch。
当然 有必要介绍一下 所有事件如下:
事件:install、activate、message、fetch、push、async。
我们已经学会 三个了。
fetch:处理浏览器发出的请求。

this.addEventListener('fetch', function (event) {
	console.log(event.request);
	event.respondWith(
		caches.match(event.request)
			.then(function (resp) {
				if (resp) {
					console.log(new Date(), 'fetch ', event.request.url, '有缓存,从缓存中取');
					return resp;
				} else {
					console.log(new Date(), 'fetch ', event.request.url, '没有缓存,网络获取');
					return fetch(event.request)
						.then(function (response) {
							return caches.open(CACHE_NAME).then(function (cache) {
								cache.put(event.request, response.clone());
								return response;
							})
						})
				}
			})
	)
});

如果匹配上我们之前的缓存,直接从缓存里面返回,
如果没有的话请求完放到缓存里面。
** 我们对缓存的操作都是,使用 CacheStorage.open(cacheName) 打开一个Cache 对象,再使用 Cache 对象的方法去处理缓存 **

return caches.open(CACHE_NAME).then(function (cache) {
	cache.put(event.request, response.clone());
	return response;
})

总结

到此,我们已经实现了一个,可以离线访问的页面。
需要掌握的就是:

  • 能够离线访问到内容是靠 CacheStorage 来存储的,

image

  • service-work,提供了一个后台的 worker,随时监听我们发出的请求。

源码

参考

lavas-baidu
贝壳技术
The Service Worker Lifecycle
Service Worker 生命周期-众成翻译
youngwind/blog
zhuanlan.zhihu
google-doc-service-workers
如果需要manifest.json
如果需要h5 Notification

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant