Service Worker

生命周期

  • 安装 (Install)

    • 当 Service Worker 被注册后,会触发安装事件。此时,Service Worker 可以进行一些初始化工作,比如缓存静态资源。
    • 安装完成后,Service Worker 会进入等待状态,等待激活。
  • 激活 (Activate)

    • 安装完成后,会触发激活事件。此时,旧的 Service Worker 如果存在,可以被卸载,新的 Service Worker 可以接管控制。
    • 这个阶段通常用于清理旧的缓存和其他不再需要的资源。
  • 运行 (Run)

    • 激活后,Service Worker 进入运行状态,可以处理网络请求和其他事件。
    • Service Worker 处于“运行中”状态时,可以通过 fetch 事件拦截网络请求,进行缓存、网络请求或其他处理。
  • 更新 (Update)

    • Service Worker 会定期检查是否有新版本可用。如果检测到新版本,会触发安装事件,并进入安装和激活的流程。
    • 可以通过 self.skipWaiting()​ 方法强制让新的 Service Worker 在激活时立即接管控制。
  • 卸载 (Unregister)

    • 用户可以主动注销 Service Worker,此时会触发相应的卸载事件。

基础流程

==注意:版本信息直接写在service worker文件中的,作为常量==

  • 第一次登陆时:

    • 注册 v1.2.0
    • 安装 v1.2.0
    • 激活 v1.2.0

  • 激活成功后:再刷新(第二次进入时)

    • 监听到fetch变化 v1.2.0
    • 重新注册 v1.2.0

  • 如果有service worker版本(代码)变化时:会自动重新注册激活安装

    • 重新注册 v1.3.0
    • 重新安装 v1.3.0
    • 重新激活 v1.3.0

每用caches.open​打开不同CACHES_NAME​时,缓存中就会有两个不同根据CACHES_NAME​命名的缓存内容

image

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const CACHES_NAME = "v1.2.1"


self.addEventListener('install', eveFnt => {
// 安装
console.log("安装完成",CACHES_NAME)
self.skipWaiting()
})

self.addEventListener('activate', async (event) => {
// 激活
console.log("激活成功", CACHES_NAME)
})

self.addEventListener('fetch', event => {
// 请求
console.log("收到fetch", CACHES_NAME)
})

缓存

基础

在install时,添加静态缓存,并使用

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
40
41
42
const RESOURCES = [
// 你的资源列表
'/index.css'
];


const CACHES_NAME = "v1.2.1"


self.addEventListener('install', eveFnt => {
// 安装
console.log("安装完成", CACHES_NAME)
self.skipWaiting()
eveFnt.waitUntil(
caches.open(CACHES_NAME).then(cache => {
return cache.addAll(RESOURCES)
})
)
})

self.addEventListener('activate', async (event) => {
// 激活
console.log("激活成功", CACHES_NAME)
})

self.addEventListener('fetch', event => {
// 请求
console.log("收到fetch", CACHES_NAME, event)

event.respondWith(
//查找缓存
caches.open(CACHES_NAME).then(function (cache) {
return caches.match(event.request).then(function (response) {
if (response) {
return response;
}

return fetch(event.request)
})
})
);
})

缓存所有

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
40
const CACHES_NAME = "v1.2.1"


self.addEventListener('install', eveFnt => {
// 安装
console.log("安装完成", CACHES_NAME)
self.skipWaiting()
})

self.addEventListener('activate', async (event) => {
// 激活
console.log("激活成功", CACHES_NAME)
})

self.addEventListener('fetch', event => {
// 请求
console.log("收到fetch", CACHES_NAME, event)

event.respondWith(
//查找缓存
caches.open(CACHES_NAME).then(function (cache) {
return caches.match(event.request).then(function (response) {
if (response) {
return response;
}

return fetch(event.request).then(response => {
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}

// 将请求和响应存入缓存
caches.open(CACHES_NAME).then(cache => cache.put(event.request, response.clone()))

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
40
41
42
43
44
45
46
47
48
49
const RESOURCES = [
// 你的资源列表
'/index.css',
'/index.js'
];


const CACHES_NAME = "v1.2.1"


self.addEventListener('install', eveFnt => {
// 安装
console.log("安装完成", CACHES_NAME)
self.skipWaiting()
})

self.addEventListener('activate', async (event) => {
// 激活
console.log("激活成功", CACHES_NAME)
})

self.addEventListener('fetch', event => {
// 请求
console.log("收到fetch", CACHES_NAME, event)

event.respondWith(
//查找缓存
caches.open(CACHES_NAME).then(function (cache) {
return caches.match(event.request).then(function (response) {
if (response) {
return response;
}

return fetch(event.request).then(response => {
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}

// 将请求和响应存入缓存
if (RESOURCES.some(resource => url.pathname === resource)) {
const responseToCache = response.clone();
caches.open(CACHES_NAME).then(cache => cache.put(event.request, responseToCache))
}
return response;
})
})
})
);
})

其他

cache.put、cache.add、cache.addAll的区别

  • cache.add(request) ​:

    • 该方法用于将请求的资源添加到缓存中。
    • 如果资源已经存在于缓存中,则不会重复添加。
    • 它会自动发出网络请求以获取资源。(相当于又fetch一次)
  • cache.put(request, response) ​:ccc

    • 该方法允许你将指定的请求和响应对存储到缓存中。
    • 无论请求是否已存在,都会进行更新。
    • 需要手动提供响应对象。
    • 适合监听fetch时使用
  • cache.addAll(requests) ​:

    • 该方法接收一个请求数组,并依次添加每个请求的资源到缓存中。
    • 它会自动处理每个请求,并且如果某个请求失败,整个操作会被取消。
    • 适合install时使用

总结来说:

  • add​ 和 addAll​ 用于从网络获取并添加资源,而 put​ 用于直接将请求和响应对存入缓存。
  • add​ 和 addAll​ 不会覆盖现有缓存,而 put​ 会。

拦截请求

==Tips: 这些mockData还可以放在IndexedDB中,更灵活的控制数据==

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const RESOURCES = [
// 你的资源列表
'/index.css'
];

const mockData = {
'/api/data1': { message: "这是数据1" },
'/api/data2': { message: "这是数据2" },
'/api/data3': { message: "这是数据3" },
'/api/data4': { message: "这是数据4" }
}

const CACHES_NAME = "v1.3.1"


self.addEventListener('install', eveFnt => {
// 安装
console.log("安装完成", CACHES_NAME)
self.skipWaiting()
})

self.addEventListener('activate', async (event) => {
// 激活
console.log("激活成功", CACHES_NAME)
})

self.addEventListener('fetch', event => {
// 请求
console.log("收到fetch", CACHES_NAME, event)

const url = new URL(event.request.url);

// 检查请求的路径是否在 mockData 中
if (mockData[url.pathname]) {
const responseData = mockData[url.pathname];
event.respondWith(
new Response(JSON.stringify(responseData), {
headers: { 'Content-Type': 'application/json' }
})
);

return
}

event.respondWith(
//查找缓存
caches.open(CACHES_NAME).then(function (cache) {
return caches.match(event.request).then(function (response) {
if (response) {
return response;
}

return fetch(event.request).then(response => {
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}

// 将请求和响应存入缓存
if (RESOURCES.some(resource => url.pathname === resource)) {
const responseToCache = response.clone();
caches.open(CACHES_NAME).then(cache => cache.put(event.request, responseToCache))
}
return response;
})
})
})
);
})