Vue 3 用 Web Worker 处理大文件解析:把卡顿的主线程解放出来
test12026-10-190 次阅读
主线程为什么会被卡住
前端解析几 MB 的 CSV、做图片像素处理、跑复杂计算时,这些同步代码会占满主线程,页面点击没反应、动画掉帧。Web Worker 能在另一条线程跑脚本,算完把结果 postMessage 回主线程,UI 始终保持响应。
用 Vite 的 worker 导入
Vite 原生支持把文件当 worker 引入,构建时会自动拆分。下面这个 worker 负责把文本按行解析成对象数组。
// parse.worker.ts
self.onmessage = (e: MessageEvent<string>) => {
const text = e.data;
const lines = text.split("\n").filter(Boolean);
const rows = lines.map((line, i) => {
const [name, score] = line.split(",");
return { index: i, name, score: Number(score) };
});
(self as unknown as Worker).postMessage(rows);
};
在组件里驱动 worker
用 ref 持有 worker 实例,onmessage 拿到结果后写回响应式状态;组件卸载时主动 terminate,避免线程泄漏。
// App.vue (script setup)
import { ref, onBeforeUnmount } from "vue";
import ParseWorker from "./parse.worker?worker";
const result = ref<any[]>([]);
const worker = new ParseWorker();
worker.onmessage = (e: MessageEvent<any[]>) => {
result.value = e.data;
};
function handleFile(file: File) {
file.text().then((t) => worker.postMessage(t));
}
onBeforeUnmount(() => worker.terminate());
传递大对象时注意结构化克隆
postMessage 默认走结构化克隆,普通对象、数组、TypedArray 都 OK;函数、DOM 节点不能传。超大二进制用 Transferable(如 ArrayBuffer)零拷贝转移所有权,避免一遍深拷贝。
const buffer = new ArrayBuffer(raw.length);
const view = new Uint8Array(buffer);
for (let i = 0; i < raw.length; i++) view[i] = raw.charCodeAt(i);
// 第二个参数把 buffer 的所有权转移给 worker,主线程不再持有
worker.postMessage({ buffer }, [buffer]);
落地要点
- worker 里拿不到 window、document,依赖 DOM 的逻辑要留在主线程。
- 频繁小消息比一次大消息更耗 postMessage 开销,按批聚合再发。
- 用 ?worker 后缀让 Vite 处理打包,别手写 new Worker(new URL(...)) 容易路径出错。
- 组件卸载务必 terminate,否则每次进入页面都会新建线程。
T
test1
文章作者
主线程为什么会被卡住 前端解析几 MB 的 CSV、做图片像素处理、跑复杂计算时,这些同步代码会占满主线程,页面点击没...
- 分类
- 技术
- 发布时间
- 2026-10-19
- 字数
- 约 1588 字
- 阅读
- 0 次
