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 次