定义
Service workers 本质上充当 Web 应用程序、浏览器与网络(可用时)之间的代理服务器
。这个 API 旨在创建有效的离线
体验,它会拦截网络请求并根据网络是否可用采取来适当的动作、更新来自服务器的的资源。它还提供入口以推送通知和访问后台同步 API。
特性
- 一个独立的
worker
线程,独立于当前网页进程,有自己独立的workercontext。 - 一旦被
install
,就永远存在,除非被手动unregister
- 用到的时候可以直接
唤醒
,不用的时候自动睡眠
- 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)
- 离线内容开发者可控
- 能向客户端
推送
消息 - 不能直接操作
DOM
- 必须在
HTTPS
或者localhost
环境下才能工作 - 异步实现,内部大都是通过
Promise
实现
生命周期
定义:简单来说,分为三个阶段:“注册(register)”、“安装(Install)”、“激活(activate)”, 还有最重要的一个拦截事件 “fetch“
// main.js
/**
* 特别说明
* @scope 表示定义service worker注册范围的URL ;service worker可以控制的URL 范围
*/
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js', {scope: './'})
.then(function(registration) {
// do something
}).catch(function(error) {
// do something
});
}
复制代码
// sw.js 文件
self.addEventListener('install', function(event) {
// do something
});
self.addEventListener('activate', function (event) {
// do something
})
/*
* The most important
*/
self.addEventListener('fetch', function (event) {
/**
1. 主要通过 CacheStorage 、Cache 等 API 操作"离线数据"和"实时数据"
2. 返回给客户端的数据主要分为:
1. Network First:网络优先策略
2. Cache First:缓存优先策略
3. Network Only:仅通过发送正常的网络请求获取资源,并将请求响应结果直接返回。
4. Cache Only:仅从缓存中读取资源。
*/
})
复制代码
工作流程
注意事项
- Service Worker 文件只在首次注册的时候执行了一次
安装
、激活
流程也只是在首次
执行 Service Worker 文件的时候进行了一次。fetch
事件- 首次注册成功的 Service Worker 没能拦截当前页面的请求。
- 非首次注册的 Service Worker 可以控制当前的页面并能拦截请求。
- 原因:为什么首次没有拦截到网络请求呢?主要是因为在 Service Worker 的注册是一个
异步
的过程,在激活完成后当前页面的请求都已经发送完成
,因为时机太晚
,此时是拦截不到任何请求的,只能等待下次
访问再进行
Service Worker 更新原理
skipWaiting
- Service Worker 一旦更新,需要等所有的终端都
关闭
之后,再重新打开页面才能激活新的 Service Worker,这个过程太复杂了。通常情况下,开发者希望当 Service Worker 一检测到更新就直接激活新的 Service Worker。
Service Worker 调试
本地数据储存
经过以上的铺垫,现在可愉快操作
离线数据
和实时数据
了,先看一段代码,后补充!
const CACHE_NAME = 'fed-cache'
const Self = globalThis
Self.addEventListener('install', function (event) {
Self.skipWaiting()
Self.caches.open(CACHE_NAME)
})
Self.addEventListener('fetch', function (event) {
/*
* 是否含有网络
* 是:进行网络请求,且更新cache数据(保持数据比较新)
* 否:进行离线缓存
*/
if (Self.navigator.onLine) {
util.fetchPut(event.request.clone())
} else {
event.respondWith(
caches.match(event.request).then((response) => {
return response
})
)
}
})
let util = {
fetchPut: function (request) {
return fetch(request).then((response) => {
const responseClone = response.clone()
if (util.noCache(response)) {
return response
}
if (request.method === 'GET') {
Self.caches.open(CACHE_NAME).then((cache) => {
cache.put(request, responseClone)
})
}
return response
})
},
noCache: function (response) {
if (
!response ||
response.status !== 200 ||
response.type !== 'basic' ||
!response.url.includes('http') ||
response.url.includes('vite')
) {
return true
}
return false
}
}
复制代码
下面将介绍操作缓存的API,我自个理解一个概念,
Cache
类似IndexedDB
都为数据库
CacheStorage API
主要操作数据库
Cache API
主要操作数据表
CacheStorage API
- CacheStorage.open() 创建/打开数据库
- CacheStorage.match() 在
所有数据库
中,检索符合条件的返回Response
对象 - CacheStorage.has() 是否含有某个数据库
- CacheStorage.delete() 删除某个数据库
- CacheStorage.keys() 遍历所有的数据库,返回数组(包含数据库名字)
Cache API
- Cache.match(request, options) 查询单条数据
- Cache.matchAll(request, options) 查询多条数据
- Cache.add(request) 添加一条数据
- Cache.addAll(requests) 添加多条数据
- Cache.put(request, response) 添加一条数据
- Cache.delete(request, options) 删除一条数据
- Cache.keys(request, options) 遍历数据表
缓存空间的使用情况
主要用于管理空间内存,达到限制要定时处理。
/**
* 查询当前缓存空间的使用情况
* 缓存资源的过期失效和清理工作,尽量避免被动触发浏览器的资源清理
*/
navigator.storage.estimate().then((estimate) => {
// 设备为当前域名所分配的存储空间总大小
console.log(estimate.quota)
// 当前域名已经使用的存储空间大小
console.log(estimate.usage)
})
复制代码
FetchEvent
- FetchEvent.respondWith() 主要防止异步操作, 和
async await
感觉一样,扩展延长fetch
事件生命周期的作用
// 错误用法
self.addEventListener('fetch', event => {
// 因fetch属于异步,存在fetch 代码已执行完毕,而setTimeout 未触发的可能性
setTimeout(() => {
event.respondWith(new Response('Hello World!'))
}, 1000)
})
复制代码
// 正确用法
// 等待 1 秒钟之后异步返回 Response 对象
event.respondWith(new Promise(resolve => {
setTimeout(() => {
resolve(new Response('Hello World!'))
}, 1000)
}))
复制代码
- ExtendableEvent.waitUntil() 方法告诉事件分发器该事件仍在进行。这个方法也可以用于检测进行的任务是否成功。在服务工作线程中,这个方法告诉浏览器事件一直进行,直至 promise 解决,浏览器不应该在事件中的异步操作完成之前终止服务工作线程。 一句话
延长生命周期
, 可用于install
、fetch
事件
addEventListener('install', event => {
const preCache = async () => {
const cache = await caches.open('static-v1');
return cache.addAll([
'/',
'/about/',
'/static/styles.css'
]);
};
event.waitUntil(preCache());
});
复制代码
通信 Clients 、Client
// main.js (主线程)
navigator.serviceWorker.addEventListener('message', (event) => {
console.log(`message: ${event.data}`)
})
// sw.js 文件
self.clients.matchAll().then(allClients=>{
allClients.forEach(client => {
client.postMessage('I am Rainy')
});
})
复制代码
最后注意的点
Cache.put
,Cache.add
和Cache.addAll
只能在GET请求下使用。- ServiceWorker, 只能
fetch
进行拦截,axios
不行
参考文章
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END