Web Worker 和 Service Worker 都是浏览器提供的 JavaScript 多线程技术,但它们的用途和工作方式有显著区别。
对比项 | Web Worker | Service Worker |
---|---|---|
用途 | 用于在后台线程执行 CPU 密集型任务,避免阻塞主线程 | 主要用于离线缓存、网络请求拦截、推送通知等 PWA(渐进式 Web 应用)功能 |
生命周期 | 由页面创建,页面关闭后终止 | 独立于页面,即使页面关闭也能运行(直到被浏览器终止) |
DOM 访问 | ❌ 不能访问 DOM | ❌ 不能访问 DOM |
网络请求 | ⚠️ 可以发起 fetch ,但不能拦截请求 | ✅ 可以拦截、修改网络请求(fetch 事件) |
存储能力 | ⚠️ 可使用 IndexedDB 、localStorage (同步 API 不可用) | ✅ 可使用 Cache API 、IndexedDB |
通信方式 | ✅ postMessage 与主线程通信 | ✅ 通过 postMessage 与页面通信,也支持 BroadcastChannel |
典型应用场景 | 大数据计算、图像处理、复杂算法 | 离线缓存、资源预加载、后台同步、推送通知 |
注册方式 | new Worker('worker.js') | navigator.serviceWorker.register('sw.js') |
作用范围 | 仅影响当前页面 | 可控制多个页面(作用域内) |
是否支持 importScripts | ✅ 支持 | ✅ 支持 |
Web Worker(专用 Worker)
-
用途:在独立线程运行脚本,防止主线程卡顿(如计算、数据处理)。
-
特点:
-
由页面创建,页面关闭后 Worker 终止。
-
不能访问 DOM、
window
、document
。 -
通过
postMessage
与主线程通信。
-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 主线程 main.js
const worker = new Worker('./worker.js');
// 接收消息
worker.onmessage = function(e) {
console.log('1111');
console.log('收到Worker消息:', e.data);
};
// 发送消息
worker.postMessage('开始计算zzz');
</script>
</body>
</html>
还需要执行的worker.js
// worker.js
self.onmessage = function(e) {
console.log('收到主线程消息:', e.data);
// 模拟耗时计算
const result = heavyCalculation();
// 发送结果
self.postMessage(result);
};
function heavyCalculation() {
// 复杂计算逻辑
let sum = 0;
for(let i = 0; i < 9999; i++) {
sum += i;
}
return sum;
}
Service Worker 实现PWA页面
-
用途:充当网络代理,实现离线缓存、资源预加载、后台同步等 PWA 功能。
-
特点:
-
独立于页面运行,即使页面关闭也能存活(用于后台同步、推送通知)。
-
可以拦截
fetch
请求,返回缓存数据。 -
必须通过 HTTPS(本地开发允许
localhost
)。
-
需要注意的是使用http-server -c-1启动服务需要使用这样的地址访问 http://127.0.0.1:8080/ 才能正常激活service worker
目录结构
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Service Worker 示例</title>
<link rel="stylesheet" href="./styles/main.css">
</head>
<body>
<h1>Service Worker 演示</h1>
<img src="./images/logo.png" alt="Logo">
<script src="./scripts/app.js"></script>
<script>
// 注册 Service Worker
if ('serviceWorker' in navigator) {
console.log('浏览器支持 Service Worker');
// 直接执行注册代码
navigator.serviceWorker.register('./sw.js')
.then(registration => {
console.log('ServiceWorker 注册成功: ', registration.scope);
})
.catch(err => {
console.error('ServiceWorker 注册失败,错误信息:', err.message, '错误堆栈:', err.stack);
});
} else {
console.log('浏览器不支持 Service Worker');
}
</script>
</body>
</html>
sw.js
const CACHE_NAME = 'my-site-cache-v1';
// 修改资源路径
const ASSETS_TO_CACHE = [
'./',
'./index.html', // 离线回退页面
'./styles/main.css',
'./scripts/app.js',
'./images/logo.png'
];
// 安装阶段 - 缓存静态资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('正在缓存核心资源');
return cache.addAll(ASSETS_TO_CACHE);
})
.catch(err => {
console.log('缓存失败: ', err);
})
);
})
// 激活阶段 - 清理旧缓存
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_NAME) {
console.log('删除旧缓存: ', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
// 拦截请求 - 缓存优先策略
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 命中缓存则返回,否则网络请求
return response || fetch(event.request);
})
);
});
// 后台同步示例(需配合 SyncManager API)
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-data') {
event.waitUntil(sendOfflineData());
}
});
async function sendOfflineData() {
// 在这里实现后台同步逻辑(如提交离线数据到服务器)
console.log('后台同步执行...');
}
styles/main.css
.img {
height: 100px;
width: 100px;
}
scripts/app.js
console.log('主应用脚本已加载1');
// 检查 Service Worker 更新
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
console.log('发现新版本 Service Worker');
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
console.log('新内容已下载,刷新页面即可使用');
// 这里可以添加 UI 提示用户刷新
} else {
console.log('内容已缓存,可离线使用');
}
}
});
});
});
}
images/logo
关键功能说明
-
预缓存静态资源
-
在
install
阶段缓存ASSETS_TO_CACHE
列表中的文件(如 HTML、CSS、JS)。
-
-
动态缓存
-
在
fetch
事件中,优先返回缓存,若无则请求网络并缓存响应。
-
-
离线回退
-
当网络请求失败且请求的是 HTML 时,返回
fallback.html
。
-
-
缓存清理
-
在
activate
阶段删除旧版本的缓存。
-
-
后台同步
-
监听
sync
事件,在恢复网络后执行离线任务(需浏览器支持)。
-
测试 Service Worker
-
首次加载
-
打开页面,检查 DevTools → Application → Service Workers 是否注册成功。
-
在 Cache Storage 中应看到缓存的静态资源。
-
-
离线测试
-
关闭网络,刷新页面,静态资源应能正常加载。
-
-
更新 Service Worker
-
修改
sw.js
或CACHE_NAME
版本号,重新加载页面触发更新。
-
注意事项
-
HTTPS 要求:生产环境必须使用 HTTPS(本地开发可用
localhost
)。 -
缓存策略:根据业务需求调整缓存逻辑(如不缓存 API 请求)。
-
作用域:
scope
决定 SW 控制的页面范围(如/app/
下的页面)。
通过这个示例,你可以快速实现一个支持离线访问的 PWA 应用!
PWA(Progressive Web App,渐进式网页应用)
是一种结合网页和原生应用优势的技术,通过现代 Web 技术提供类似原生应用的体验。以下是它的核心特点和解释:
1. 核心特点
-
可离线使用:通过 Service Worker 缓存资源,即使无网络也能访问。
-
安装到桌面:用户可将网页添加到主屏幕,像独立应用一样启动(无需应用商店)。
-
响应式设计:适配手机、平板、电脑等多种设备。
-
推送通知:支持消息推送(类似原生 App)。
-
安全性:必须运行在 HTTPS 环境下,确保数据安全。
2. 关键技术
-
Service Worker:后台运行的脚本,管理缓存和离线功能。
-
Web App Manifest:JSON 文件,定义应用名称、图标、启动样式等。
-
HTTPS:强制要求,保证安全性。
-
App Shell 架构:快速加载核心界面框架,提升性能。
3. 优势 vs 传统网页/原生应用
对比项 | PWA | 传统网页 | 原生应用 |
---|---|---|---|
安装方式 | 浏览器添加到主屏幕 | 仅浏览器访问 | 应用商店下载 |
离线功能 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
更新 | 自动(Service Worker) | 实时刷新 | 需应用商店审核 |
开发成本 | 低(Web 技术) | 最低 | 高(需多平台开发) |
4. 典型应用场景
-
电商(如 AliExpress、京东 Lite)
-
社交媒体(Twitter Lite)
-
新闻博客(内容型网站)
-
工具类应用(计算器、天气预报)
总结
场景 | 选择 |
---|---|
需要后台计算(如大数据处理) | Web Worker |
需要离线缓存、拦截网络请求 | Service Worker |
需要推送通知、后台同步 | Service Worker |
需要多线程并行计算 | Web Worker 或 Shared Worker(跨页面通信) |
两者可以结合使用,例如用 Web Worker 处理数据,用 Service Worker 缓存结果。