本节内容是对于MDN上相关章节的总结记录。
重点归纳总结 Service Worker
生态中所有相关的 API
。
1.Main Thread
在 Main Thread
中涉及到的顶级 API
是 ServiceWorkerContainer
。
ServiceWorkerContainer
继承自 EventTarget
。
1-1.navigator.serviceWorker
我们通常不会直接使用全局构造函数 ServiceWorkerContainer
,而是使用一个已有实例 navigator.serviceWorker
。
console.log(navigator.serviceWorker instanceof ServiceWorkerContainer)
1-2.实例属性
1-2-1.controller
该属性通常可用于判断 serviceWorker
是否已激活。
如果 navigator.serviceWorker
的 state
是 activating
或 activated
,那么 navigator.serviceWorker.controller
属性会返回一个 serviceWorker
对象。
如果 serviceWorker
未运行或者浏览器强制刷新(shift
+ reload
),那么此 controller
会返回 null
。
console.log(navigator.serviceWorker.controller)
1-2-2.ready
该属性提供了 serviceWorker
激活后的后置方法。
navigator.serviceWorker.ready
会返回一个 promise
对象。
如果 promise
是 resolved
则会返回 registration
实例,否则 rejected
会返回 error
。
MDN上说该属性只会 resolved
。
navigator.serviceWorker.ready.then(registration => {
console.log(`A service worker is active: ${registration.active}`)
})
1-3.实例方法
1-3-1.getRegistration()
navigator.serviceWorker.getRegistration()
方法用来获取指定 clientURL
下的一个注册服务。
navigator.serviceWorker.getRegistration().then(registration => {
console.log(`getRegistration: ${registration}`)
})
其中,clientURL
如果没有指定,则默认是当前 Main Thread JavaScript
文件执行目录。
// 获取默认目录下
navigator.serviceWorker.getRegistration()
// 获取/app下
navigator.serviceWorker.getRegistration('/app')
1-3-2.getRegistrations()
navigator.serviceWorker.getRegistrations()
方法用来获取指定 clientURL
下的所有注册服务。
navigator.serviceWorker.getRegistrations().then(registrations => {
console.log(`getRegistrations: ${registrations}`)
})
registrations
是数组形式,集合了所有注册服务。
// 获取默认目录下
navigator.serviceWorker.getRegistrations()
// 获取/app下
navigator.serviceWorker.getRegistrations('/app')
1-3-3.register()
navigator.serviceWorker.register()
方法用来手动注册服务。
基础语法:
register(scriptURL)
register(scriptURL, options)
scriptURL
用来指定serviceWorker
脚本地址。options
包含以下可选配置:scope
作用域,默认是./
type
服务类型,classic
或module
updateViaCache
更新机制 TODO: 确认下这里的具体机制作用
navigator.serviceWorker.register('./sw.js').then(registration => {
console.log(`register: ${registration}`)
}).catch(err => {
console.error(`register fail: ${err}`)
})
假设上述代码运行在 example.com/index.html
,则 example.com/sw.js
注册作用域会在 example.com/
目录下生效。
navigator.serviceWorker.register('./sw.js', {
scope: '/app/'
}).then(registration => {
console.log(`register: ${registration}`)
}).catch(err => {
console.error(`register fail: ${err}`)
})
假设上述代码运行在 example.com/index.html
,则 example.com/sw.js
注册作用域会在 example.com/app/
目录下生效。
1-3-4.startMessages()
默认情况下,如果 ServiceWorker
中的 postMessage
向 Main Thread
传递消息时,Main Thread
会在 DOMContentLoaded
加载完成后,逐个处理 message
。
可以利用 startMessages()
方法提前在 DOMContentLoaded
之前执行处理 message
。
navigator.serviceWorker.startMessages()
1-4.事件监听
1-4-1.controllerchange
在 Main Thread
中监听 Main Thread
的 navigator.serviceWorker
的 controller
发生改变。
navigator.serviceWorker.addEventListener('controllerchange', e => {
console.log('controllerchange', e)
})
TIP
笔者测试了下,在 navigator.serviceWorker.controller
发生改变时,该事件始终没有被触发。
1-4-2.message
在 Main Thread
中监听 Worker Thread
中利用 postMessage
传递的信息。
navigator.serviceWorker.addEventListener('message', e => {
console.log(`message: `, e.data)
})
2.ServiceWorkerRegistration
当 navigator.serviceWorker
执行 register()
方法注册之后,会返回 ServiceWorkerRegistration
的 registration
实例对象。
ServiceWorkerRegistration
同样继承自 EventTarget
。
2-1.实例属性
2-1-1.active
该属性会返回 state
为 activating
或 activated
的 ServiceWorker
。
默认为 null
。
2-1-2.installing
该属性会返回 state
为 installing
的 ServiceWorker
。
默认为 null
。
2-1-3.waiting
该属性会返回 state
为 installed
的 ServiceWorker
。
默认为 null
。
2-1-4.navigationPreload
该属性会返回NavigationPreloadManager的一个实例。
通常可用于控制资源的预加载。
2-1-5.pushManager
该属性会返回PushManager的一个实例。
2-1-6.scope
该属性会返回 ServiceWorker
的注册作用域。
譬如在 http://127.0.0.1:8086/web-worker-service/index.html
下执行:
navigator.serviceWorker.register('./sw.js').then(registration => {
console.log(`register: `, registration.scope)
}).catch(err => {
console.error(`register fail: ${err}`)
})
其 registration.scope
会打印 http://127.0.0.1:8086/web-worker-service/
。
2-1-7.updateViaCache
该属性会返回 ServiceWorker
使用的 HTTP
缓存策略。
imports
默认值。只有importScripts
使用HTTP
缓存。all
所有资源都使用HTTP
缓存。none
所有资源都不使用HTTP
缓存。
TODO: 确认下该属性的实际作用
2-2.实例方法
2-2-1.getNotifications()
getNotifications()
方法用于获取当前页面上的所有通知。
// sw.js
self.addEventListener('notificationclick', event => {
event.notification.close(); // 关闭被点击的通知
// 在这里可以添加处理通知点击的逻辑
console.log('Notification clicked:', event.notification.data);
// 例如,打开一个链接
clients.openWindow('https://example.com');
});
// 在某个地方需要获取当前页面上的通知
function handleNotifications() {
self.registration.getNotifications().then(notifications => {
notifications.forEach(notification => {
console.log('Existing notification:', notification.data);
// 在这里可以执行其他操作,如修改通知内容等
});
});
}
// 调用获取通知的函数
handleNotifications();
2-2-2.showNotification()
showNotification()
方法用于展示指定通知。
// sw.js
self.addEventListener('push', event => {
const options = {
body: event.data.text(), // 推送消息的正文
icon: 'path/to/icon.png', // 通知图标
data: {
// 自定义数据,可用于处理通知点击等事件
customKey: 'customValue'
}
};
event.waitUntil(
self.registration.showNotification('Push Notification', options)
);
});
self.addEventListener('notificationclick', event => {
event.notification.close(); // 关闭通知
// 处理通知点击事件,可以执行自定义逻辑
console.log('Notification clicked:', event.notification.data);
// 例如,打开一个链接
clients.openWindow('https://example.com');
})
2-2-3.unregister()
unregister()
方法支持手动取消注册服务。
返回值是一个 promise
,如果取消成功,则 resolved
值为 true
,否则为 false
。
navigator.serviceWorker.register('./sw.js')
.then(registration => {
registration.unregister().then(boolean => {
console.log(`unregister is ${boolean}`)
})
})
2-2-4.update()
update()
方法会更新 Service Worker
,它会 fetch
定义好的 Script URL
,进而比对字节。如果前后两者不一致的话,会安装新的 Service Worker
。
2-3.事件
2-3-1.updatefound
updatefound
事件会在 Service Worker
发生更新时触发。
也就是说当 ServiceWorkerRegistration.installing
属性取得新的 Service Worker
时触发。
3.Worker Thread
Service Worker
的线程作用域是 ServiceWorkerGlobalScope
。
ServiceWorkerGlobalScope
继承自 WorkerGlobalScope
。
3-1.实例属性
3-1-1.clients
self.clients
属性会返回Clients的一个实例。
3-1-2.registration
self.registration
属性会返回ServiceWorkerRegistration的一个实例。
3-2.实例方法
3-2-1.skipWaiting()
Service Worker
的基础生命周期流程如下:
installing
waiting
active
调用 self.skipWaiting()
方法会使得 Service Worker
跳过 waiting
,从而处于 active
激活阶段。
3-3.事件
3-3-1.install
install
事件监听,在 Service Worker
首次注册时触发,用于执行一次性的安装任务,例如缓存资源。
当 Service Worker
被安装后,它就会进入等待激活状态,直到没有任何当前使用中的页面使用旧的 Service Worker
为止。
// sw.js
self.addEventListener('install', event => {
event.waitUntil(
caches.open('my-cache').then(cache => {
return cache.addAll([
'/',
'/index.html',
'/styles.css',
'/script.js'
]);
})
);
});
3-3-2.activate
activate
事件监听,当 Service Worker
处于 activating
或 activated
状态时触发。
即 activate
事件在 Service Worker
安装后首次激活以及之后每次切换到新版本时触发。
activate
事件通常用于执行清理旧缓存、处理旧版本的 Service Worker
等任务。
// sw.js
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
// 清理旧版本缓存
return Promise.all(
cacheNames.filter(cacheName => {
return cacheName.startsWith('my-cache') && cacheName !== 'my-cache';
}).map(cacheName => {
return caches.delete(cacheName);
})
);
})
);
});
3-3-3.fetch
fetch
事件监听,当 Main Thread
发生网络请求时触发,不限于 ajax
、fetch
或者静态资源请求(html
、js
、css
等等)。
通过监听 fetch
事件,可以实现自定义的缓存策略、资源代理、或其他网络请求相关的逻辑。
// sw.js
self.addEventListener('fetch', event => {
// 拦截并处理网页发起的所有网络请求
console.log('Fetching:', event.request.url);
// 示例:自定义缓存策略
event.respondWith(customCacheStrategy(event.request));
});
function customCacheStrategy(request) {
// 在这里可以实现自定义的缓存策略
// 返回一个 Promise,用于响应网络请求
return caches.match(request).then(cachedResponse => {
// 如果缓存中有匹配的响应,则直接返回缓存
if (cachedResponse) {
console.log('Cache hit:', request.url);
return cachedResponse;
}
// 否则,发起网络请求并缓存响应
console.log('Cache miss, fetching from network:', request.url);
return fetch(request).then(response => {
// 注意:需要将响应克隆一份,因为 Response 对象是单次使用的
let responseToCache = response.clone();
caches.open('my-cache').then(cache => {
cache.put(request, responseToCache);
});
return response;
});
});
}
3-3-4.message
message
事件监听,用来对信息通信作出响应。
// main.js
if (navigator.serviceWorker) {
navigator.serviceWorker.register('service-worker.js')
navigator.serviceWorker.addEventListener('message', (event) => {
// event is a MessageEvent object
console.log(`The service worker sent me a message: ${event.data}`)
})
navigator.serviceWorker.ready.then((registration) => {
registration.active.postMessage('Hi service worker')
})
}
// sw.js
self.addEventListener('message', (event) => {
// event is an ExtendableMessageEvent object
console.log(`The client sent me a message: ${event.data}`)
event.source.postMessage('Hi client')
});
3-3-5.notificationclick
notificationclick
事件监听,用来处理通知横幅点击的效果。
self.addEventListener('notificationclick', (event) => {
console.log('On notification click: ', event.notification.tag)
event.notification.close()
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(
clients
.matchAll({
type: 'window'
})
.then((clientList) => {
for (const client of clientList) {
if (client.url === '/' && 'focus' in client) return client.focus()
}
if (clients.openWindow) return clients.openWindow('/')
})
)
})
3-3-6.notificationclose
notificationclose
事件监听,用来处理通知横幅关闭的效果。
self.addEventListener('notificationclose', event => {
// do something
})
3-3-7.push
push
事件监听,用来处理服务端向客户端推送的信息。
self.addEventListener(
'push',
(event) => {
let message = event.data.json()
switch (message.type) {
case 'init':
doInit()
break
case 'shutdown':
doShutdown()
break
}
},
false
)
在服务端,可以使用 node.js
封装好的库web-push来进行消息推送。
3-3-8.pushsubscriptionchange
pushsubscriptionchange
事件是由浏览器自动触发的,而不是由开发者手动触发的。
这个事件会在以下情况下被浏览器触发:
订阅状态变化: 当用户订阅或取消订阅推送通知时,
pushsubscriptionchange
事件将被触发。推送服务端点变化: 如果推送服务端点(
Push Service Endpoint
)发生变化,例如由于推送服务商更改了服务端点,pushsubscriptionchange
事件也会被触发。
譬如,以下代码实现了,在订阅过期时,重新发起订阅请求,并将最新的订阅信息同步到服务端:
self.addEventListener(
'pushsubscriptionchange',
(event) => {
const conv = (val) =>
btoa(String.fromCharCode.apply(null, new Uint8Array(val)))
const getPayload = (subscription) => ({
endpoint: subscription.endpoint,
publicKey: conv(subscription.getKey('p256dh')),
authToken: conv(subscription.getKey('auth'))
})
const subscription = self.registration.pushManager
.subscribe(event.oldSubscription.options)
.then((subscription) =>
fetch('register', {
method: 'post',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
old: getPayload(event.oldSubscription),
new: getPayload(subscription)
})
})
)
event.waitUntil(subscription)
},
false
)
3-3-9.sync
Service Worker
本身是支持离线工作的。
而 sync
事件监听,则是提供了在网络连接恢复时,将客户端收集的数据同步到服务端。
// main.js
navigator.serviceWorker.ready.then(registration => {
return registration.sync.register('mySyncEvent')
})
// sw.js
self.addEventListener('sync', (event) => {
if (event.tag === 'mySyncEvent') {
event.waitUntil(syncDataWithServer())
}
})
function syncDataWithServer() {
// 在这里执行数据同步操作,将离线时收集的数据发送到服务器
const cachedData = JSON.parse(localStorage.getItem('cachedData')) || []
return fetch('/sync-data-endpoint', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(cachedData)
})
.then(response => response.json())
.then(data => {
console.log('Data synced with server:', data)
})
.catch(error => {
console.error('Sync failed:', error)
})
}
4.CacheStorage
CacheStorage
顾名思义,是一个 Cache
仓库。
它用来管理具体的 Cache
对象。可通过全局变量 caches
来访问。
4-1.实例方法
4-1-1.match()
match()
方法用来判断 CacheStorage
中是否有匹配的 Cache
。
match(request, options)
参数介绍如下:
request
一个Request对象或者URL
字符串。options
ignoreSearch
ignoreMethod
ignoreVary
cacheName
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((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((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((cache) => {
cache.put(event.request, responseClone)
})
return response
})
.catch(() => caches.match('/gallery/myLittleVader.jpg'))
}
})
)
})
4-1-2.has()
has()
方法用以判断 CacheStorage
中是否包含指定 Cache
。
has(cacheName)
参数介绍如下:
cacheName
目标cache
名
caches
.has('v1')
.then((hasCache) => {
if (!hasCache) {
someCacheSetupFunction()
} else {
caches.open('v1').then((cache) => cache.addAll(myAssets))
}
})
.catch(() => {
// Handle exception here.
})
4-1-3.open()
open()
方法用以打开或创建 CacheStorage
中的具体 Cache
。
open(cacheName)
参数介绍如下:
cacheName
目标cache
名
self.addEventListener('install', (event) => {
event.waitUntil(
caches
.open('v1')
.then((cache) =>
cache.addAll([
'/',
'/index.html',
'/style.css',
'/app.js',
'/image-list.js',
'/star-wars-logo.jpg',
'/gallery/bountyHunters.jpg',
'/gallery/myLittleVader.jpg',
'/gallery/snowTroopers.jpg'
])
)
)
})
4-1-4.delete()
delete()
方法用以删除 CacheStorage
中的具体 Cache
。
它的基础语法是 delete(cacheName)
。
如果在 CacheStorage
中成功删除了指定 Cache
,则 promise
的 resolved
结果是 true
。
如果在 CacheStorage
中未找到指定的 Cache
,则 promise
的 resolved
结果是 false
。
self.addEventListener('activate', (event) => {
const cachesToKeep = ['v2']
event.waitUntil(
caches.keys().then((keyList) =>
Promise.all(
keyList.map((key) => {
if (!cachesToKeep.includes(key)) {
return caches.delete(key)
}
})
)
)
)
})
4-1-5.keys()
keys()
方法用以获取 CacheStorage
中的所有 Cache
键。
this.addEventListener('activate', (event) => {
const cacheAllowlist = ['v2']
event.waitUntil(
caches.keys().then((keyList) =>
Promise.all(
keyList.map((key) => {
if (!cacheAllowlist.includes(key)) {
return caches.delete(key)
}
})
)
)
)
})
5.Cache
5-1.实例方法
(async function() {
await caches.delete('v1')
const cache = await caches.open('v1')
const pathA = await cache.add('/')
const searchPathA = await cache.add('/?query=1')
const pathB = await cache.addAll(['/web-worker-service', '/web-worker-service?query=1'])
const pathC = await cache.put('/?query=2', new Response())
const match = await cache.match('/', {
ignoreSearch: true
})
const matchAll = await cache.matchAll('/', {
ignoreSearch: true
})
const keys = await cache.keys()
console.log({
pathA,
searchPathA,
pathB,
pathC
})
console.log('match', match)
console.log('matchAll', matchAll)
console.log('keys', keys)
})()
5-1-1.match()
match()
方法用来匹配指定 request
对应的第一个 response
。
cache.match(request, options)
5-1-2.matchAll()
matchAll()
方法用来匹配指定 request
对应的所有 response
。
cache.matchAll(request, options)
5-1-3.add()
add()
方法用来向 cache
中添加目标 request
对象或者 URL
。
在执行之后,浏览器会自动根据传入的 request
对象或者 URL
进行解析。
然后将与 request
对象或者 URL
的 response
关联。
cache.add(request)
5-1-4.addAll()
addAll()
方法用来向 cache
中添加一系列 request
对象或者 URL
。
addAll()
与 add()
的区别,在于 addAll()
添加的是数组形式:
cache.addAll([request, request, ...])
5-1-5.put()
前文中我们介绍了 add()
与 addAll()
方法。
如果我们要添加自定义 cache
的话,可以使用 put()
方法。
cache.put(request, response)
譬如:
cache.put('/', new Response())
5-1-6.delete()
delete()
方法相对于 add()
方法,用来删除 cache
。
cache.delete(request, options)
5-1-7.keys()
keys()
方法用来获取 cache
的所有键。
cache.keys()
6.Clients
Clients
是在 Service Worker
中使用的 API
。
可以通过 self.clients
来访问。
6-1.实例方法
6-1-1.claim()
claim()
方法可以将一个激活的 Service Worker
在对应的作用域scope下设置为所有客户端 clients
的 controller
。
该方法会触发 navigator.serviceWorker
上的 controllerchange
事件。
譬如以下代码,在 Service Worker
的 activate
事件中,调用 claim()
方法。
这会使得页面不必重新加载,就能将最新的 Service Worker
代码生效:
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim())
})
TIP
Only the active worker can claim clients.
What is the use of self.clients.claim()?
6-1-2.get()
get()
方法能够在 clients
获取给定 id
所对应的 client
。
const id = '778decd0-180b-4f2d-9e92-f4e45cecc305'
self.clients.get(id).then((client) => {
self.clients.openWindow(client.url)
})
6-1-3.matchAll()
matchAll()
方法用来根据指定条件匹配 client
列表。
基础语法如下:
matchAll(options)
options
可配置:
includeUncontrolled
返回所有Service Worker
客户端,即使还未受控制。默认值为false
。type
客户端类型。默认值为window
。window
worker
sharedworker
all
譬如:
self.clients.matchAll({
includeUncontrolled: true,
type: 'all'
}).then(clientList => {
for (const client of clientList) {
if (client.url === 'index.html') {
clients.openWindow(client)
}
}
})
6-1-4.openWindow()
openWindow()
方法用来打开新的页面。
浏览器为了防止滥用,只有用户动作产生(有术语称作瞬时激活),才能调用此 API
。否则会有 DOMException: Not allowed to open a window.
。
// service worker
self.addEventListener('notificationclick', function(event) {
event.waitUntil(
// 打开新窗口
clients.openWindow('/page-to-open')
.then(function(client) {
// 在这里使用新窗口的客户端对象
if (client) {
client.focus()
}
})
)
})
7.Client
7-1.实例属性
7-1-1.frameType
frameType
属性表明了 client
上下文类型。
常见的有如下几种类型:
auxiliary
表示辅助框架,通常对应于<object>
或<embed>
元素中的文档。top-level
表示顶级框架,通常对应于整个页面或标签页。nested
表示嵌套框架,通常对应于 iframe 中的文档。
7-1-2.id
id
属性表明了 client
唯一标识。
类似于 UUID 形式,譬如:e59bbf1c-69cf-44f2-a4e7-b28736041110
。
7-1-3.type
type
属性表明了 client
类型。
常见的有如下几种类型:
window
worker
sharedworker
TIP
如果 type
是 window
的话,那么此 Client
可以被详细的称作 WindowClient。
WindowClient
继承自 Client
,相对来说,多了 focused
及 focus()
等属性或方法。
7-1-4.url
url
属性表明了 client
的地址。
7-2.实例方法
7-2-1.postMessage()
postMessage()
方法允许 Service Worker
向 Client
(可能是 window
、worker
、sharedWorker
) 传递消息。
addEventListener('fetch', (event) => {
event.waitUntil(
(async () => {
// Exit early if we don't have access to the client.
// Eg, if it's cross-origin.
if (!event.clientId) return
// Get the client.
const client = await self.clients.get(event.clientId)
// Exit early if we don't get the client.
// Eg, if it closed.
if (!client) return
// Send a message to the client.
client.postMessage({
msg: 'Hey I just got a fetch from you!',
url: event.request.url
})
})()
)
})
8.ExtendableEvent
ExtendableEvent
是 Service Worker API
中的一个基类,用于表示可以被扩展的事件。
它是其他 Service Worker
事件的基类,包括 ExtendableMessageEvent
、FetchEvent
、InstallEvent
、ActivateEvent
等,这些事件都继承自 ExtendableEvent
。
ExtendableEvent
提供了一个 waitUntil()
方法,该方法接受一个 Promise
,并延长事件的寿命,使得事件处理程序可以在 Promise
完成之前继续执行。
这使得在事件处理程序中执行异步操作成为可能,例如在 FetchEvent
中进行网络请求或在 InstallEvent
中预缓存资源。
8-1.实例方法
8-1-1.waitUntil()
waitUntil()
是 Service Worker API
中的一个方法,主要用于在事件处理程序中延长事件的寿命。
self.addEventListener('install', function(event) {
// 使用 waitUntil 方法接收一个 Promise
event.waitUntil(
// 执行异步操作,例如预缓存资源
caches.open('v1').then(function(cache) {
return cache.addAll([
'/static/resource1',
'/static/resource2'
// 添加其他静态资源
])
})
)
})
在上述代码中,waitUntil()
接收了一个 Promise
,该 Promise
是由 caches.open()
返回的。
只有在该 Promise
完成(即缓存操作完成)之前,Service Worker
才会认为 install
事件已经处理完成。
需要注意的是,如果传递给 waitUntil()
的 Promise
被拒绝(rejected
),Service Worker
会认为事件处理失败,从而影响整个安装、激活或其他事件的生命周期。
9.ExtendableMessageEvent
ExtendableMessageEvent
是 Service Worker API
中的一个基类,它继承自 ExtendableEvent
,用于表示从其他上下文(例如页面上下文)发送到 Service Worker
的消息事件。
这个类的主要用途是处理通过 postMessage API
发送的消息。
9-1.实例属性
9-1-1.data
data
属性用来获取 postMessage
方法传递的数据信息。
// Main Thread
navigator.serviceWorker.controller.postMessage('Hello from the page!')
// Service Worker Thread
self.addEventListener('message', function(event) {
// event 是 ExtendableMessageEvent 的实例
console.log('Received message:', event.data)
})
9-1-2.lastEventId
lastEventId
属性用来获取最后一次接收的事件的标识符(ID
)。
self.addEventListener('message', function(event) {
// event 是 ExtendableMessageEvent 的实例
const lastEventId = event.lastEventId
console.log('Last Event ID:', lastEventId)
})
9-1-3.origin
origin
属性用来获取客户端来源 origin
地址。
self.addEventListener('message', function(event) {
// event 是 ExtendableMessageEvent 的实例
const origin = event.origin
console.log('Origin:', origin)
})
9-1-4.ports
ports
属性用来获取与消息关联的 MessagePort
对象数组。
这个属性通常在与 postMessage API
和 MessageChannel
一起使用时发挥作用。
MessagePort
是一个双向通信通道,允许在两个上下文之间发送消息。
当通过 postMessage
发送消息时,如果消息中包含了 MessageChannel
,那么与该消息关联的 MessagePort
对象就会被传递给接收方。
接收方可以通过 event.ports
属性来获取这些传递的 MessagePort
对象。
self.addEventListener('message', function(event) {
// event 是 ExtendableMessageEvent 的实例
const ports = event.ports
console.log('Ports:', ports)
})
9-1-5.source
source
属性用来获取利用 postMessage
发送消息的 Client
。
self.addEventListener('message', function(event) {
// event 是 ExtendableMessageEvent 的实例
const source = event.source
console.log('Source:', source)
})
10.FetchEvent
FetchEvent
是在 Service Worker
中拦截和处理 fetch
请求或者资源网络请求(JS
、CSS
等)的事件类型。
该 API
提供了 event.respondWith()
方法,来供开发者自定义响应。
10-1.实例属性
10-1-1.clientId
clientId
属性用来获取当前 Service Worker
控制着的 Client
标识。
self.addEventListener('fetch', (event) => {
console.log(event.clientId)
})
10-1-2.replacesClientId
replacesClientId
属性用来获取在页面导航期间被替换的客户端 ID
。
假如,从 pageA
导航到 pageB
,则 replacesClientId
是与 pageA
相关联的客户端 ID
。
如果从 about:blank
页面导航到 pageB
,则 replacesClientId
是一个空字符串。因为 about:blank
页面是会被重用,而不是替换。
另外,如果请求不属于浏览器导航(譬如是 ajax
或 fetch
),那么 replacesClientId
也会是一个空字符串。
self.addEventListener('fetch', (event) => {
console.log(event.replacesClientId)
})
10-1-3.resultingClientId
resultingClientId
属性用来获取在页面导航期间被替换后的客户端 ID
。
假如,从 pageA
导航到 pageB
,则 resultingClientId
是与 pageB
相关联的客户端 ID
。
self.addEventListener('fetch', (event) => {
console.log(event.resultingClientId)
})
10-1-4.handled
handled
属性用来获取 FetchEvent
已经被处理完后的节点。
该属性会返回一个 promise
,当该 promise
的状态改变,证明 FetchEvent
已经被处理。
self.addEventListener('fetch', (event) => {
event.respondWith(
(async function () {
const response = await doCalculateAResponse(event.request)
event.waitUntil(
(async function () {
await doSomeAsyncStuff() // optional
// Wait for the event to be consumed by the browser
await event.handled
return doFinalStuff() // Finalize AFTER the event has been consumed
})()
)
return response
})()
)
})
10-1-5.request
request
属性用来获取 fetch
事件的请求对象。
self.addEventListener('fetch', (event) => {
console.log(event.request)
})
10-1-6.preloadResponse
preloadResponse
属性用来获取 preload 预加载响应。
self.addEventListener('fetch', (event) => {
event.respondWith(
(async () => {
// Respond from the cache if we can
const cachedResponse = await caches.match(event.request)
if (cachedResponse) return cachedResponse
// Else, use the preloaded response, if it's there
const response = await event.preloadResponse
if (response) return response
// Else try the network.
return fetch(event.request)
})()
)
})
10-2.实例方法
10-2-1.respondWith()
respondWith()
方法用于提供自定义的响应,以替代默认的网络请求。
基础语法如下:
event.respondWith(response)
其中 response
是一个表示要返回的响应的对象。它可以是来自网络的真实响应,也可以是通过 Response
构造函数创建的自定义响应,也可以是一个 resolved
结果为 Response
的 Promise
。
self.addEventListener('fetch', function(event) {
// 尝试从缓存中获取匹配的响应
event.respondWith(
caches.match(event.request).then(function(cachedResponse) {
// 如果缓存中存在匹配的响应,直接返回缓存
if (cachedResponse) {
return cachedResponse
}
// 否则,发起网络请求并将响应添加到缓存
return fetch(event.request).then(function(networkResponse) {
// 需要克隆响应对象,因为它是流式的,只能读取一次
const clonedResponse = networkResponse.clone()
// 将网络响应添加到缓存
caches.open('my-cache').then(function(cache) {
cache.put(event.request, clonedResponse)
})
// 返回网络响应
return networkResponse
})
})
)
})
11.InstallEvent(Deprecated)
MDN上显示该**构造函数 API
**已经废弃了,因此不再过多介绍。
在 Service Worker
中的 install
中可以打印 InstallEvent
实例如下: