在前端开发中,性能优化一直是我们关注的重点。当遇到复杂的计算任务时,主线程可能会被阻塞,导致页面卡顿,影响用户体验。而Web Worker的出现,为我们解决这类问题提供了一个很好的方案。Web Worker允许在后台线程中执行脚本,与主线程并行运行,从而避免阻塞主线程。然而,传统Web Worker的引入方式存在显著局限性:需指定独立脚本文件路径,在 Vue、React 等工程化框架中使用时,往往需要额外安装依赖包,并在 Webpack、Vite 等构建工具中进行复杂配置,不仅增加开发成本,还可能因配置不当引发兼容性问题。如今,借助ObjectURL和DataURL两种创新方案,开发者无需复杂配置即可快速调用Web Worker。

1.无需指定文件创建 Web Worker 的基础案例

ObjectURL 实现方法​

ObjectURL,即对象 URL,也称为 Blob URL,它指向内存中的Blob对象。若使用ObjectURL,代码如下:

const str = `console.log(123)`;​
const blob = new Blob([str], { type: 'application/javascript' });​
const url = URL.createObjectURL(blob);​
const worker = new Worker(url);

这里先将包含Web Worker逻辑的字符串str创建为Blob对象,再通过URL.createObjectURL生成对应临时的ObjectURL,最后传递给Worker构造函数。ObjectURL的优势在于兼容性良好,且能动态生成,适用于复杂逻辑的Web Worker。不过,使用完毕后必须调用URL.revokeObjectURL(url)释放资源,否则会造成内存泄漏。​

DataURL 实现方法​

DataURL,即数据 URL,它将数据直接编码到 URL 中:

const str = `console.log(123)`;​
const url = `data:application/javascript;utf-8,${str}`;​
const worker = new Worker(url);

DataURL无需创建Blob对象,直接将Web Worker的逻辑代码以特定格式编码进 URL。这种方式简单直接,无需额外资源管理,但受限于 URL 长度,如果Web Worker代码量过大,可能导致 URL 超长,引发问题。

2.项目中使用无文件指定创建 Web Worker​

下面以一个简单的定时器小案例展示Web Worker在工程化项目中的使用:

1.新建src\worker\time.worker.ts文件

const timeWorker = () => {
    onmessage = (e) => {
        const { type, delay = 1000 } = e.data
        let timer = null
        const startTime = new Date().getTime()
        let durtion = 0
        if (type === 'start') {
            timer = setInterval(() => {
                durtion += delay
                postMessage({ durtion, dif: new Date().getTime() - startTime })
            }, delay)
        } else if (type === 'stop') {
            timer && clearInterval(timer)
        }
    }
}

const SwitchToUrl = (fn: { (): void}) => {
    const funcCode = fn.toString()
    const context = funcCode.slice(funcCode.indexOf('{') + 1, funcCode.lastIndexOf('}'))
    // const blob = new Blob([context], { type: 'application/javascript' })
    // return URL.createObjectURL(blob)
    return `data:application/javascript;utf-8,${context}`
}

export default SwitchToUrl(timeWorker)

SwitchToUrl函数的作用是将传入的函数转换为可供Web Worker使用的 URL。它首先获取传入函数的字符串表示funcCode,然后提取函数体部分context。
这里实际上是导出了一个url,可以直接传递给Worker构造函数。

2.以 Vue3 为例集成无配置 Web Worker

<template>
  <div class="worker-demo">
    <h3>Web Worker计时器示例</h3>
    <div class="controls">
      <button @click="startWorker" :disabled="isRunning">开始计时</button>
      <button @click="stopWorker" :disabled="!isRunning">停止计时</button>
    </div>
    <div class="results">
      <p>持续时间: {{ duration }}ms</p>
      <p>时间差: {{ timeDiff }}ms</p>
    </div>
  </div>
</template>

<script setup>
import { ref, onUnmounted } from 'vue';
import timeWorker from '@/worker/time.worker'; // 导入上面的worker模块

// 创建Worker实例
const worker = new Worker(timeWorker);

// 状态管理
const duration = ref(0);
const timeDiff = ref(0);
const isRunning = ref(false);

// 消息处理
worker.onmessage = (e) => {
  duration.value = e.data.durtion;
  timeDiff.value = e.data.dif;
};

// 错误处理
worker.onerror = (error) => {
  console.error('Worker error:', error);
};

// 控制方法
const startWorker = () => {
  worker.postMessage({ type:'start', delay: 1000 });
  isRunning.value = true;
};

const stopWorker = () => {
  worker.postMessage({ type:'stop' });
  isRunning.value = false;
};

// 组件销毁时清理资源 销毁线程
onUnmounted(() => {
  worker.postMessage({ type:'stop' });
  worker.terminate();
});
</script>