修复了 Service Worker 的 fetch 事件处理器中 `caches.match()` 可能返回 undefined 导致的 "Failed to convert value to 'Response'" 错误。 ## 问题 - 当网络请求失败且缓存中没有匹配资源时,`caches.match()` 返回 undefined - `event.respondWith()` 必须接收 Response 对象,不能接收 undefined - 导致浏览器控制台报错: TypeError: Failed to convert value to 'Response' ## 修复内容 - 添加请求过滤:只处理同源的 GET 请求 - 添加缓存回退检查:如果 caches.match() 返回空,返回 408 错误响应 - 确保 event.respondWith() 始终接收有效的 Response 对象 ## 代码变更 public/service-worker.js: - 添加同源请求检查 (startsWith(self.location.origin)) - 添加 GET 请求过滤 - 添加 .then(response => ...) 处理缓存未命中情况 - 返回 408 Request Timeout 响应作为最终后备 ## 验证 - ✅ npm run build 构建成功 - ✅ Service Worker 不再报错 - ✅ 网络请求正常工作
116 lines
3.8 KiB
JavaScript
116 lines
3.8 KiB
JavaScript
// public/service-worker.js
|
|
/**
|
|
* Service Worker for Browser Notifications
|
|
* 主要功能:支持浏览器通知的稳定运行
|
|
*/
|
|
|
|
const CACHE_NAME = 'valuefrontier-v1';
|
|
|
|
// Service Worker 安装事件
|
|
self.addEventListener('install', (event) => {
|
|
console.log('[Service Worker] Installing...');
|
|
// 跳过等待,立即激活
|
|
self.skipWaiting();
|
|
});
|
|
|
|
// Service Worker 激活事件
|
|
self.addEventListener('activate', (event) => {
|
|
console.log('[Service Worker] Activating...');
|
|
// 立即接管所有页面
|
|
event.waitUntil(self.clients.claim());
|
|
});
|
|
|
|
// 通知点击事件
|
|
self.addEventListener('notificationclick', (event) => {
|
|
console.log('[Service Worker] Notification clicked:', event.notification.tag);
|
|
|
|
event.notification.close();
|
|
|
|
// 获取通知数据中的链接
|
|
const urlToOpen = event.notification.data?.link;
|
|
|
|
if (urlToOpen) {
|
|
event.waitUntil(
|
|
clients.matchAll({ type: 'window', includeUncontrolled: true })
|
|
.then((windowClients) => {
|
|
// 查找是否已有打开的窗口
|
|
for (let client of windowClients) {
|
|
if (client.url.includes(window.location.origin) && 'focus' in client) {
|
|
// 聚焦现有窗口并导航到目标页面
|
|
return client.focus().then(client => {
|
|
return client.navigate(urlToOpen);
|
|
});
|
|
}
|
|
}
|
|
// 如果没有打开的窗口,打开新窗口
|
|
if (clients.openWindow) {
|
|
return clients.openWindow(urlToOpen);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
// 通知关闭事件
|
|
self.addEventListener('notificationclose', (event) => {
|
|
console.log('[Service Worker] Notification closed:', event.notification.tag);
|
|
});
|
|
|
|
// Fetch 事件 - 基础的网络优先策略
|
|
self.addEventListener('fetch', (event) => {
|
|
// 只处理同源请求,跳过跨域请求
|
|
if (!event.request.url.startsWith(self.location.origin)) {
|
|
return;
|
|
}
|
|
|
|
// 只处理 GET 请求
|
|
if (event.request.method !== 'GET') {
|
|
return;
|
|
}
|
|
|
|
// 对于通知相关的资源,使用网络优先策略
|
|
event.respondWith(
|
|
fetch(event.request)
|
|
.catch(() => {
|
|
// 网络失败时,尝试从缓存获取
|
|
return caches.match(event.request)
|
|
.then(response => {
|
|
// 如果缓存也没有,返回一个基本的网络错误响应
|
|
if (!response) {
|
|
return new Response('Network error', {
|
|
status: 408,
|
|
statusText: 'Request Timeout',
|
|
headers: new Headers({
|
|
'Content-Type': 'text/plain'
|
|
})
|
|
});
|
|
}
|
|
return response;
|
|
});
|
|
})
|
|
);
|
|
});
|
|
|
|
// 推送消息事件(预留,用于未来的 Push API 集成)
|
|
self.addEventListener('push', (event) => {
|
|
console.log('[Service Worker] Push message received:', event);
|
|
|
|
if (event.data) {
|
|
const data = event.data.json();
|
|
const options = {
|
|
body: data.body || '您有新消息',
|
|
icon: data.icon || '/favicon.png',
|
|
badge: '/favicon.png',
|
|
data: data.data || {},
|
|
requireInteraction: data.requireInteraction || false,
|
|
tag: data.tag || `notification_${Date.now()}`,
|
|
};
|
|
|
|
event.waitUntil(
|
|
self.registration.showNotification(data.title || '价值前沿', options)
|
|
);
|
|
}
|
|
});
|
|
|
|
console.log('[Service Worker] Loaded successfully');
|