"主线程太忙,画面卡成 PPT,工人(Worker)快上!"
在现代 Web 开发中,尤其是 WebGL 场景中,性能就是一切。Three.js 让我们轻松打造炫酷 3D 世界,但一旦你加入大量逻辑计算,比如粒子模拟、路径规划、物理引擎,主线程立刻哀嚎:"我已经尽力了,别再来了!"
这时,我们就该请出我们的幕后英雄 :Web Workers
!
🏭 主线程与工人线程:谁干活,谁看图?
想象一下,主线程就像是舞台上的演员,负责把画面渲染得漂漂亮亮。而 Web Worker 呢?就是后台的舞台工人,负责干重活脏活累活,比如数学运算、模拟碰撞、做 AI 路径规划。
🎭 主线程的职责
- 初始化场景和摄像机
- 渲染动画帧(
renderer.render(scene, camera)
) - 响应用户交互(鼠标、键盘、触摸)
🛠 Worker 的职责
- 处理耗时的计算(比如几千个粒子的位移计算)
- 数据处理(例如模型预处理、路径规划等)
- 保证主线程轻盈,从而提升 FPS
✨ Web Workers 的用法(精简版)
我们先看看最基本的 Web Worker 使用方式。
1. 创建 Worker 文件 physics.worker.js
php
// physics.worker.js
self.onmessage = function (event) {
const data = event.data;
// 模拟繁重的逻辑,比如粒子运动
const result = data.positions.map(p => ({
x: p.x + Math.sin(Date.now() * 0.001 + p.x),
y: p.y + Math.cos(Date.now() * 0.001 + p.y),
z: p.z
}));
// 把结果回传给主线程
self.postMessage({ positions: result });
};
2. 主线程中启动 Worker
ini
// main.js
const worker = new Worker(new URL('./physics.worker.js', import.meta.url));
const particlePositions = [/* 若干粒子坐标 */];
worker.onmessage = (e) => {
const updated = e.data.positions;
// 把更新的坐标同步到 Three.js 的几何体中
updateParticles(updated);
};
// 每帧都向 worker 发送坐标
function animate() {
requestAnimationFrame(animate);
worker.postMessage({ positions: particlePositions });
renderer.render(scene, camera);
}
🧬 updateParticles 实现示意
ini
function updateParticles(updated) {
for (let i = 0; i < updated.length; i++) {
const p = updated[i];
geometry.attributes.position.setXYZ(i, p.x, p.y, p.z);
}
geometry.attributes.position.needsUpdate = true;
}
📦 Vite 用户注意:使用 Worker 的正确姿势
javascript
// 如果你用的是 Vite,需要通过 URL 引入 Worker
import Worker from './physics.worker.js?worker';
const worker = new Worker();
🚀 性能提升背后的真相:为什么 FPS 提高了?
主线程是单线程的,渲染和 JS 执行共享同一个执行栈。一旦你做了重逻辑,页面就会像老牛拖车那样画面一卡一卡。
Web Worker 的好处:
- 并行执行:计算工作被放到了另一个线程中
- 非阻塞渲染:渲染帧不被干扰,画面更流畅
- 可扩展性更好:你可以跑多个 worker 执行不同逻辑任务
⚠️ 踩坑指南
坑点 | 解释 |
---|---|
❌ 直接访问 DOM | Worker 无法访问 DOM,Three.js 只能跑在主线程 |
❌ 不能直接传函数 | 数据传递必须是可序列化对象,如 JSON |
❌ 大数据频繁通信 | 大对象频繁传输会拖慢主线程,可用 Transferable 优化 |
✅ 使用 OffscreenCanvas (高阶) |
在某些浏览器中可以实现完全的多线程渲染(实验性) |
🌊 拓展玩法(进阶)
- 用 Web Worker 跑物理引擎(Ammo.js / Cannon-es)
- 把点云或模型数据加载解析放入 Worker
- 用 Worker 跑 AI 路径规划、BFS、A* 等搜索算法
🧪 小实验:同时计算 1 万个粒子的涡旋运动
javascript
// worker 处理涡旋轨迹(模拟 CPU 密集)
self.onmessage = ({ data }) => {
const result = data.positions.map((p, i) => {
const t = Date.now() * 0.001 + i;
return {
x: Math.sin(t) * 10 + p.x,
y: Math.cos(t) * 10 + p.y,
z: p.z
};
});
self.postMessage({ positions: result });
};
主线程只需安安静静地:更新 geometry,render!
🧙♂️ 总结:主线程干视觉,Worker 干数学
"你干计算,我搞浪漫"------这就是主线程与 Worker 的完美协作。
Web Worker 就像是一位隐居山林的老程序员,不问江湖,只管苦算;而主线程则是城市里俊俏的画家,涂抹绚丽的视觉世界。两者一动一静,一文一武,合力才能撑起性能优雅的 WebGL 世界。