/home/ORE$ npm run dev
ore@0.0.1 dev ts-node-dev src/index.ts
[INFO] 10:39:46 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.2, typescript ver. 5.5.3) Server is running on port 3000 GET / 304 3.184 ms - - GET /serviceWorker.js 404 4.394 ms - 155
???
最近在重新搞express ORE项目时,我遇到了一个让我非常困惑的问题。浏览器总是自动访问一个名为 serviceWorker.js 的文件,这是个什么东西?之前在nuxt项目中我没注意,我以为这是某种攻击或漏洞扫描,毕竟在现代互联网环境中,安全问题不容忽视。然而今天我发现这拼写怪熟悉的,这好像是Service Worker!没错,就是那个水过好多文章背过多好次HTML5新特性中的Service Worker!!!
什么是 Service Worker?
Service Worker 是一种运行在浏览器背后的独立线程,主要用于控制页面的缓存和网络请求。它让开发者能够创建更高效、更可靠的网络应用。Service Worker 的主要功能包括以下几个方面:
- 缓存管理:Service Worker 可以缓存关键资源,从而提高应用的加载速度和离线可用性。
- 推送通知:通过 Service Worker,应用可以在后台接收并显示推送通知。
- 后台同步:允许应用在网络连接恢复后自动同步数据。
- 拦截网络请求:可以拦截和处理网络请求,提供更灵活的响应策略。
为什么会自动访问 serviceWorker.js?
当你使用一些现代前端框架或工具(如 Vue CLI、Nuxt、Create React App、Workbox 等)时,它们通常会默认生成并注册一个 Service Worker 文件。这是因为这些工具旨在帮助开发者构建渐进式 Web 应用(PWA),而 Service Worker 是实现 PWA 的关键技术之一。
Service Worker 的工作原理
Service Worker 的生命周期主要包括以下几个阶段:
- 安装(Install):当浏览器首次加载 Service Worker 时,会触发安装事件。在这个阶段,开发者通常会预缓存一些静态资源。
- 激活(Activate):安装完成后,Service Worker 会进入激活阶段。在这个阶段,可以清理旧缓存等操作。
- 运行(Running):激活后,Service Worker 开始拦截网络请求,并根据开发者定义的策略进行响应。
一个简单的 Service Worker 示例
下面是一个简单的 Service Worker 示例代码,展示了如何缓存静态资源并在离线时提供缓存响应:
// serviceWorker.js
const CACHE_NAME = 'my-cache-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js',
'/images/logo.png'
];
// 安装阶段,缓存静态资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// 激活阶段,清理旧缓存
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
// 拦截网络请求并提供缓存响应
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 缓存中有匹配的资源,返回缓存
if (response) {
return response;
}
// 缓存中没有匹配的资源,进行网络请求
return fetch(event.request);
})
);
});
如何在项目中注册 Service Worker
在项目中注册 Service Worker 非常简单。以下是一个在 JavaScript 文件中注册 Service Worker 的示例:
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/serviceWorker.js')
.then(registration => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(error => {
console.log('Service Worker registration failed:', error);
});
});
}
场景示例
缓存 API 请求响应
self.addEventListener('fetch', event => {
if (event.request.url.includes('/api/')) {
event.respondWith(
caches.open('api-cache').then(cache => {
return fetch(event.request).then(response => {
cache.put(event.request, response.clone());
return response;
}).catch(() => {
return caches.match(event.request);
});
})
);
}
});
实现简单的消息推送
self.addEventListener('push', event => {
const data = event.data.json();
const options = {
body: data.body,
icon: 'images/notification.png',
badge: 'images/badge.png'
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
后台数据同步
Service Worker 可以在网络连接恢复后自动同步数据。这里是一个简单的示例:
self.addEventListener('sync', event => {
if (event.tag === 'sync-data') {
event.waitUntil(
fetch('/sync-endpoint').then(response => {
return response.json();
}).then(data => {
// 处理同步数据
})
);
}
});
响应来自其他源的资源请求
Service Worker 可以拦截和处理跨域请求。例如,从一个 CDN 获取资源:
self.addEventListener('fetch', event => {
if (event.request.url.includes('https://cdn.example.com')) {
event.respondWith(
fetch(event.request).then(response => {
return caches.open('cdn-cache').then(cache => {
cache.put(event.request, response.clone());
return response;
});
}).catch(() => {
return caches.match(event.request);
})
);
}
});
集中接收计算成本高的数据更新
Service Worker 可以集中处理高计算成本的数据更新,例如地理位置或陀螺仪数据:
self.addEventListener('message', event => {
if (event.data.type === 'update-geolocation') {
// 处理地理位置更新
clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage({
type: 'geolocation-updated',
data: event.data.geolocation
});
});
});
}
});
在客户端进行 CoffeeScript、LESS 等模块编译
虽然这主要用于开发目的,但 Service Worker 可以在客户端进行一些编译工作:
importScripts('https://cdn.jsdelivr.net/npm/less@4.1.1/dist/less.min.js');
self.addEventListener('message', event => {
if (event.data.type === 'compile-less') {
less.render(event.data.lessCode).then(output => {
event.source.postMessage({
type: 'less-compiled',
css: output.css
});
});
}
});
后台服务钩子
Service Worker 可以用作后台服务钩子,进行特定的后台任务:
self.addEventListener('fetch', event => {
if (event.request.url.endsWith('/background-hook')) {
event.respondWith(
fetch(event.request).then(response => {
// 处理后台任务
return response;
})
);
}
});
自定义模板用于特定 URL 模式
Service Worker 可以为特定的 URL 模式提供自定义响应:
self.addEventListener('fetch', event => {
if (event.request.url.endsWith('/custom-template')) {
event.respondWith(
new Response('<html><body><h1>Custom Template</h1></body></html>', {
headers: { 'Content-Type': 'text/html' }
})
);
}
});
性能增强,预取用户可能需要的资源
Service Worker 可以预取用户可能需要的资源,提高应用性能:
self.addEventListener('install', event => {
event.waitUntil(
caches.open('prefetch-cache').then(cache => {
return cache.addAll([
'/images/photo1.jpg',
'/images/photo2.jpg',
'/images/photo3.jpg'
]);
})
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
结语
通过这次经历,我不仅消除了对 serviceWorker.js 的误解,还学习到了 Service Worker 的强大功能。Service Worker 不仅不是攻击或漏洞,反而是提升 Web 应用性能和用户体验的重要工具。希望这篇文章能帮助其他开发者更好地理解和使用 Service Worker。
通过本文,读者老爷们应该对 Service Worker 和其在现代 Web 开发中的重要性有了更深入的理解。无论是缓存管理、推送通知、后台同步,还是拦截网络请求,Service Worker 都提供了强大的功能,帮助开发者构建更高效、更可靠的 Web 应用。希望这篇文章能为你在项目中使用 Service Worker 提供一些有用的参考。