本节内容是对于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或moduleupdateViaCache更新机制 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 的基础生命周期流程如下:
installingwaitingactive
调用 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字符串。optionsignoreSearchignoreMethodignoreVarycacheName
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。windowworkersharedworkerall
譬如:
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 类型。
常见的有如下几种类型:
windowworkersharedworker
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 实例如下:
