前言:Web Worker 是 HTML5 提供的多线程解决方案,可以将耗时的计算逻辑放到独立的后台线程中运行,避免阻塞主线程(UI 线程),解决页面卡顿、交互无响应的问题,是前端性能优化的核心手段之一。
一、核心基础认知
1. 为什么需要 Worker
JavaScript 是单线程语言,所有代码都在主线程串行执行。一旦遇到耗时较长的计算(大数据排序、复杂算法、大文件解析),主线程会被占用,导致页面无法响应用户交互、动画掉帧,甚至出现 “页面无响应” 提示。
Web Worker 的作用就是:开辟独立的后台线程,和主线程并行运行,专门处理耗时任务,完成后再把结果传回主线程,全程不影响页面渲染和交互。
2. 能力与限制
支持:
- 执行 JS 代码,拥有独立的全局上下文
- 使用 fetch / XMLHttpRequest 发送网络请求
- 使用 setTimeout / setInterval 定时器
- 访问 IndexedDB、缓存 API
- 处理二进制数据(ArrayBuffer、Blob)
- 导入外部 JS 脚本
不支持:
- 不能访问 DOM(无法操作 document、window、页面元素
- 不能直接调用主线程的变量和函数
- 不能使用 alert、confirm 等浏览器弹窗 API
- 加载的 Worker 文件必须遵守同源策略
3. 三类 Worker 对比
| 类型 | 全称 | 生命周期 | 共享范围 | 核心用途 |
|---|---|---|---|---|
| 专用 Worker | Dedicated Web Worker | 随创建它的页面而生,页面关闭即销毁 | 仅属于创建它的单个页面 / 标签页 | 单页面的耗时计算、数据处理(最常用) |
| 共享 Worker | Shared Worker | 同域下多个页面共享,最后一个页面关闭才销毁 | 同域下多个标签页共享 | 多标签页数据共享、统一状态管理 |
| 服务 Worker | Service Worker | 独立于页面,浏览器后台常驻 | 同域下所有页面 | PWA 离线缓存、请求拦截、后台同步 |
二、当前学习重点: Web Worker 基础用法
1.创建 worker线程
在public公共文件夹下创建heavy-calc.worker.js,路径如下
内容如下:
self.onmessage = function(e) { const { count } = e.data const result = heavyCalculate(count) self.postMessage({ result }) } // 加重运算:加入三角函数,单次循环计算量更大,更容易触发长卡顿 function heavyCalculate(num) { let sum = 0 for (let i = 0; i < num; i++) { sum += Math.sqrt(i) * Math.sin(i) * Math.cos(i) } return sum }2.主线程使用
创建 worker 只需要通过 new 调用 Worker(path, options) 构造函数即可,它接收两个参数
const calcWorker = new Worker('/static/workers/heavy-calc.worker.js',{})| 参数 | 说明 |
|---|---|
| path | 有效的js脚本的地址,必须遵守同源策略。无效的js地址或者违反同源策略,会抛出SECURITY_ERR 类型错误 |
| options.type | 可选,用以指定 worker 类型。该值可以是 classic 或 module。 如未指定,将使用默认值 classic |
| options.credentials | 可选,用以指定 worker 凭证。该值可以是 omit, same-origin,或 include。如果未指定,或者 type 是 classic,将使用默认值 omit (不要求凭证) |
| options.name | 可选,在 DedicatedWorkerGlobalScope 的情况下,用来表示 worker 的 scope 的一个 DOMString 值,主要用于调试目的。 |
注意:如果你想在相对路径中使用worker,需要注意导入方式,相对路径如图:
此方法为:Vite 原生支持的标准方式,会自动处理路径、打包、热更新,生产环境也不会出问题,完全规避路径坑
import HeavyCalcWorker from './workers/heavy-calc.worker.js?worker' const calcWorker = new HeavyCalcWorker()3.主线程与 worker 线程都是通过 postMessage 方法来发送消息,以及监听 message 事件来接收消息
// 主线程 const runWorker = () => { console.time('Worker计算耗时') calcWorker.postMessage({ count: CALC_COUNT }) } calcWorker.onmessage = (e) => { result.value = e.data.result console.timeEnd('Worker计算耗时') } calcWorker.onerror = (err) => { console.error('Worker报错:', err.message) } // worker线程 self.onmessage = function(e) { const { count } = e.data const result = heavyCalculate(count) self.postMessage({ result }) }ok,到这里,一个简单的计算worker就创建成功了!
4.监听错误信息
web worker 提供两个事件监听错误,error 和 messageerror。这两个事件的区别是:
| 事件 | 触发时机 | 描述 |
|---|---|---|
| error | Worker 脚本执行阶段 | 文件 404、语法错误、变量不存在、代码抛错 |
| messageerror | postMessage 数据传输阶段 | 循环引用、传函数 / DOM、转移后重复使用 ArrayBuffer |
5.关闭 worker 线程
// 组件销毁清理 onUnmounted(() => { calcWorker.terminate() self.close() })6. Worker 线程引用其他js文件(基于 ESModule模式)
复杂的计算场景,不想把所有代码都放入worker线程时,可以直接使用 module 模式初始化 worker 线程。
主线程写法:
// 相对路径导入写法 import HeavyCalcWorker from './workers/heavy-calc.worker.js?worker&type=module' const calcWorker = new HeavyCalcWorker() // 绝对路径导入写法 const calcWorker = new Worker('/static/workers/heavy-calc.worker.js',{ type: 'module'})worker线程
import moment from 'moment' console.log(moment().format('YYYY-MM-DD HH:mm:ss'))打印结果