Service Worker是运行在浏览器后台的独立线程脚本,它不依赖页面,能够拦截和处理网络请求,配合Cache API可以实现资源的缓存与读取,是实现web离线应用的核心技术。它让web应用在无网络或者弱网络环境下依然可以正常加载核心功能,大幅提升用户体验。

Service Worker的核心特性
Service Worker有以下几个关键特性,理解这些特性有助于我们更好地使用它实现离线功能:
- 独立于主线程运行,不会阻塞页面渲染,也不会影响页面的性能
- 只能运行在HTTPS环境或者localhost本地开发环境下,保障通信安全
- 拥有完整的生命周期,包括注册、安装、激活、拦截请求等阶段
- 无法直接操作DOM,需要通过postMessage和页面进行通信
- 可以拦截作用域内的所有网络请求,自主决定返回缓存内容还是发起网络请求
实现离线应用的基本步骤
1. 注册Service Worker
首先需要在页面的主脚本中注册Service Worker,指定Service Worker脚本的路径和作用域。作用域决定了Service Worker可以拦截哪些路径下的请求,默认是脚本所在目录及其子目录。
// 检查浏览器是否支持Service Worker
if ('serviceWorker' in navigator) {
// 页面加载完成后注册Service Worker
window.addEventListener('load', function() {
// 注册sw.js作为Service Worker脚本,作用域为当前目录
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('Service Worker注册成功,作用域为: ', registration.scope);
}).catch(function(error) {
console.log('Service Worker注册失败: ', error);
});
});
}
2. 编写Service Worker安装逻辑
Service Worker的install事件会在注册后首次下载脚本时触发,我们可以在这个阶段预缓存应用需要的静态资源,比如HTML、CSS、JS、图片等核心文件。
// sw.js 文件内容
// 定义缓存名称和需要预缓存的资源列表
const CACHE_NAME = 'offline-cache-v1';
const PRECACHE_URLS = [
'/',
'/index.html',
'/style.css',
'/main.js',
'/logo.png'
];
// 监听install事件,预缓存资源
self.addEventListener('install', function(event) {
// 等待缓存操作完成后再结束install阶段
event.waitUntil(
// 打开指定名称的缓存
caches.open(CACHE_NAME).then(function(cache) {
console.log('预缓存资源开始');
// 将预缓存列表中的资源全部加入缓存
return cache.addAll(PRECACHE_URLS);
}).then(function() {
console.log('预缓存资源完成');
// 强制新的Service Worker直接进入激活状态,跳过等待
return self.skipWaiting();
})
);
});
3. 激活阶段清理旧缓存
当新的Service Worker被激活时,会触发activate事件,我们可以在这个阶段清理旧版本的缓存,避免缓存占用过多存储空间。
// 监听activate事件,清理旧缓存
self.addEventListener('activate', function(event) {
event.waitUntil(
// 获取所有缓存的名称
caches.keys().then(function(cacheNames) {
// 遍历所有缓存,删除不是当前版本的缓存
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheName !== CACHE_NAME) {
console.log('删除旧缓存: ', cacheName);
return caches.delete(cacheName);
}
})
);
}).then(function() {
// 让新的Service Worker立即接管所有页面
return self.clients.claim();
})
);
});
4. 拦截请求并返回缓存
Service Worker的fetch事件会拦截作用域内的所有网络请求,我们可以在这里实现缓存优先或者网络优先的策略,决定返回缓存内容还是发起网络请求。
// 监听fetch事件,处理请求
self.addEventListener('fetch', function(event) {
// 只处理GET请求,其他请求直接放行
if (event.request.method !== 'GET') {
return;
}
// 响应请求,优先返回缓存内容,缓存不存在再发起网络请求
event.respondWith(
caches.match(event.request).then(function(response) {
// 如果缓存中有对应的响应,直接返回缓存
if (response) {
return response;
}
// 缓存中没有,发起网络请求
return fetch(event.request).then(function(networkResponse) {
// 如果网络请求成功,将响应克隆一份存入缓存
if (networkResponse && networkResponse.status === 200) {
// 克隆响应,因为响应只能被读取一次
const responseClone = networkResponse.clone();
caches.open(CACHE_NAME).then(function(cache) {
cache.put(event.request, responseClone);
});
}
return networkResponse;
}).catch(function() {
// 网络请求失败,可以返回一个离线兜底页面
return caches.match('/offline.html');
});
})
);
});
常见缓存策略对比
不同的业务场景适合不同的缓存策略,以下是几种常见的策略对比:
| 策略名称 | 实现逻辑 | 适用场景 |
|---|---|---|
| 缓存优先 | 先查缓存,缓存有则返回,无则请求网络并更新缓存 | 静态资源、不常变化的页面 |
| 网络优先 | 先请求网络,成功则返回并更新缓存,失败则返回缓存 | 实时性要求高的数据、频繁更新的内容 |
| 仅缓存 | 只返回缓存内容,不发起网络请求 | 完全离线的功能模块 |
| 网络仅 | 只发起网络请求,不使用缓存 | 不需要离线支持的功能 |
开发注意事项
- Service Worker的更新机制是:修改sw.js文件内容后,浏览器会下载新的脚本,新脚本进入等待状态,直到所有旧页面关闭后才会激活,开发时可以通过
skipWaiting和clients.claim跳过等待 - 缓存的资源需要设置合理的过期策略,避免用户一直使用旧版本的内容
- 不要在Service Worker中执行耗时操作,避免影响请求拦截的效率
- 测试时可以在Chrome的Application面板中查看Service Worker的状态、缓存内容,也可以手动触发更新、注销等操作
- 如果应用有用户登录态相关的请求,不要缓存这类请求,避免数据泄露
总结
使用Service Worker实现离线应用的核心就是利用它的请求拦截能力和Cache API管理缓存,按照注册、安装预缓存、激活清理、拦截请求返回缓存的流程开发,再结合业务场景选择合适的缓存策略,就可以让web应用具备离线使用的能力。虽然Service Worker的生命周期和调试有一定门槛,但掌握之后可以大幅提升web应用的可用性和用户体验,是渐进式web应用(PWA)的核心技术之一。
Service_Worker离线应用javascript缓存策略修改时间:2026-07-13 19:15:40