5月27日 14:00

Web Worker 有哪几种类型?Dedicated、Shared、Service 怎么选?

三种 Worker,三种用途

浏览器里能叫"Worker"的有三种,干的事完全不一样:

类型一句话定位和页面关系典型用途
Dedicated Worker后台计算线程一对一,页面关了它就销毁排序、解析、图像处理
Shared Worker多页面共享的后台线程多对一,所有同源页面共享跨标签页状态同步
Service Worker网络代理 + 离线缓存独立生命周期,页面关了还活着PWA、离线、请求拦截

别搞混——Dedicated Worker 是拿来干活的,Shared Worker 是拿来共享的,Service Worker 是拿来代理网络的。

Dedicated Worker:用得最多的那个

绝大多数时候你说的"Web Worker"就是它。一个页面创建,只有这个页面能用,页面关了 Worker 也跟着销毁。

javascript
// 创建 const worker = new Worker('worker.js'); // 双向通信 worker.postMessage({ type: 'start', data: payload }); worker.onmessage = (e) => console.log('结果:', e.data); // 关闭 worker.terminate();

也可以用 Blob URL 创建内联 Worker,不用单独的 JS 文件:

javascript
const code = ` self.onmessage = (e) => { const result = heavyCalc(e.data); self.postMessage(result); }; `; const worker = new Worker(URL.createObjectURL(new Blob([code], { type: 'text/javascript' })));

Dedicated Worker 的生命周期很简单:创建 → 运行 → terminate 或页面关闭。没有什么"激活""等待"状态,不需要管理复杂状态机。

Shared Worker:跨标签页的共享线程

多个同源标签页可以共用同一个 Shared Worker 实例。适合做跨页面状态同步——比如用户在标签页 A 加了购物车商品,标签页 B 实时看到数量更新。

javascript
// 每个页面都这样创建,浏览器会复用同一个实例 const worker = new SharedWorker('shared-worker.js'); // 注意:SharedWorker 用 port 通信,不是直接 onmessage worker.port.start(); worker.port.postMessage({ type: 'cart-update', item: 'iPhone 17' }); worker.port.onmessage = (e) => { console.log('收到:', e.data); };

Worker 端也不一样,用 onconnect 接收新连接:

javascript
// shared-worker.js const clients = []; self.onconnect = (e) => { const port = e.ports[0]; clients.push(port); port.onmessage = (event) => { // 广播给所有连接的页面 clients.forEach(client => { client.postMessage(event.data); }); }; };

Shared Worker 的坑

  • 调试困难——Chrome DevTools 里要单独打开 Shared Worker 的调试面板(chrome://inspect/#workers
  • 所有连接断开后 Worker 才会销毁,不是最后一个页面关了就立刻死
  • port.start() 容易忘写,忘写了消息收不到但也不报错

Service Worker:不是普通 Worker

Service Worker 是三种里最特殊的。它不是用来做计算的,而是浏览器的网络代理层:

  • 拦截请求:页面发出的 fetch 请求先经过 Service Worker,可以改写响应、返回缓存
  • 离线支持:把资源缓存下来,断网时也能访问
  • 推送通知:即使页面没打开,也能收到服务端推送
  • 后台同步:网络恢复时自动重试失败的请求
javascript
// 注册 navigator.serviceWorker.register('/sw.js'); // sw.js self.addEventListener('install', (event) => { // 安装时预缓存资源 event.waitUntil( caches.open('v1').then(cache => cache.addAll(['/index.html', '/app.js'])) ); }); self.addEventListener('fetch', (event) => { // 拦截请求,先查缓存 event.respondWith( caches.match(event.request).then(cached => cached || fetch(event.request)) ); });

Service Worker 的生命周期和其他两种完全不同:

shell
安装(install) → 激活(activate) → 运行中 ↑ ↓ 等待(waiting) ← 更新发现

关键区别:Service Worker 在页面关闭后仍然存活,浏览器会在需要时唤醒它。这也是为什么它能处理推送通知和后台同步。

Service Worker 不能做的事:同步 XHR、访问 DOM、访问 localStorage。和 Dedicated Worker 一样受 API 限制,但更严格——连 self.localStorage 都没有,只能用 Cache API 和 IndexedDB。

怎么选

场景选哪个
页面内耗时计算(排序、解析)Dedicated Worker
多标签页共享状态Shared Worker
离线缓存、请求拦截Service Worker
推送通知Service Worker
后台数据同步Service Worker
图像/音视频处理Dedicated Worker

一个常见错误:用 Shared Worker 做计算密集型任务。Shared Worker 的设计初衷是共享状态,不是共享算力。如果多个页面同时往一个 Shared Worker 发计算任务,它还是单线程处理,反而互相等待。

另一个常见错误:把 Service Worker 当普通 Worker 用。Service Worker 的生命周期管理复杂,它会在不可预期的时间被浏览器唤醒和终止。在它里面做长耗时计算是不靠谱的——可能算到一半就被杀了。

标签:Web Worker