将耗时超16ms且不依赖DOM的纯数值动效计算(如物理模拟、贝塞尔插值、状态机运动、路径变形)移入Web Worker,主线程仅负责渲染,通过Transferable对象零拷贝传递数组、扁平化消息通信,并在组件卸载时终止Worker、添加错误监听与帧序校验,确保流畅不卡顿。

直接把动效的数学计算放进 Web Worker,主线程就能继续响应滚动、点击和 requestAnimationFrame——这不是让动画变快,而是让页面不卡。
哪些动效计算适合搬进 Worker
不是所有动效逻辑都需要 Worker。真正该挪走的,是那些单次执行超过 16ms、且不依赖 DOM 的纯数值任务:
- 物理模拟:弹簧阻尼、粒子系统轨迹、碰撞检测(仅输出位置/速度向量)
- 贝塞尔插值与样条拟合:多控制点高阶曲线参数求解、时间到位移的映射表生成
- 复杂状态机驱动的运动:带延迟、条件跳转、多层嵌套的状态迁移与帧级输出
- 路径变形与顶点动画:SVG 路径点重采样、Canvas 图形顶点批量偏移计算(如波纹、扭曲)
数据怎么传才不拖慢主线程
Worker 和主线程不共享内存,传错数据类型反而比计算还慢。关键在“结构化 + 零拷贝”:
- 只传轻量配置:起始时间戳、当前输入状态(如鼠标 x/y、按键标志)、参数(弹性系数、摩擦力、帧步长)
- 大数组用 Transferable:比如位移序列用
Float32Array,主线程调用worker.postMessage({ data }, [data.buffer]),之后data自动置为 null - 避免嵌套对象:把二维轨迹压成一维数组 + 元信息,例如
{ type: 'path', width: 2, length: 500, data: new Float32Array([...]) }
主线程怎么接住并渲染结果
Worker 不画图,只算数;主线程不计算,只渲染。两者必须解耦:
- Worker 每帧输出一个扁平对象:
{ frame: 142, x: 127.4, y: 89.1, scale: 0.98, rotation: 0.32 } - 主线程用
ref或useState缓存最新帧,requestAnimationFrame中读取并应用到元素(CSS transform / Canvas draw / SVG transform) - 支持渐进式:若动效需 2 秒完成,Worker 可每 50ms 发一次中间帧,主线程立刻绘制,用户看到的是连续过程,而非等待后突变
别漏掉收尾和容错
Worker 是常驻线程,不关就会吃内存,出错也不报错:
- 组件卸载时调用
worker.terminate()(Vue 用onBeforeUnmount,React 用useEffect cleanup) - Worker 脚本开头加
self.onerror = e => self.postMessage({ type: 'error', message: e.message }) - 主线程收到消息先校验
data.frame是否大于上一帧,丢弃乱序或过期结果


















