在 Vue 3 中使用 工作者线程

在 Vue 3 中测试工作者线程

1. Vue 3 项目中快速启动测试


方法一:使用 Vue CLI 或 Vite 创建项目

bash

bash 复制代码
# 使用 Vite 创建 Vue 3 项目
npm create vue@latest worker-test
# 或
npm init vue@latest

# 选择配置:
# ✔ Project name: worker-test
# ✔ TypeScript: No
# ✔ JSX Support: No
# ✔ Vue Router: No
# ✔ Pinia: No
# ✔ Vitest: No
# ✔ End-to-End Testing: No
# ✔ ESLint: No
# ✔ Prettier: No

cd worker-test
npm install
npm run dev

方法二:直接创建最小化项目

bash

bash 复制代码
mkdir vue-worker-test
cd vue-worker-test

# 创建 package.json
cat > package.json << 'EOF'
{
  "name": "vue-worker-test",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.4.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.0.0",
    "vite": "^5.0.0"
  }
}
EOF

# 安装依赖
npm install

# 创建项目结构

2. Vue 3 项目结构

复制代码
vue-worker-test/
├── public/
│   └── worker.js          # Worker 文件(如果放在 public)
├── src/
│   ├── workers/           # Worker 目录
│   │   └── calculator.worker.js
│   ├── composables/       # 组合式函数
│   │   └── useWorker.js
│   ├── components/
│   │   └── WorkerTest.vue
│   ├── App.vue
│   ├── main.js
│   └── style.css
├── index.html
├── vite.config.js
└── package.json

3. Vue 3 组件中使用 Worker

App.vue

vue

javascript 复制代码
<template>
  <div id="app">
    <h1>Vue 3 Web Worker 测试</h1>
    <WorkerTest />
  </div>
</template>

<script setup>
import WorkerTest from './components/WorkerTest.vue'
</script>

WorkerTest.vue 组件

vue

javascript 复制代码
<template>
  <div class="worker-test">
    <div class="controls">
      <button @click="startWorker" :disabled="workerRunning">
        {{ workerRunning ? 'Worker运行中' : '启动Worker' }}
      </button>
      <button @click="sendMessage" :disabled="!workerRunning">
        发送计算任务
      </button>
      <button @click="stopWorker" :disabled="!workerRunning">
        停止Worker
      </button>
      <button @click="runInMainThread">
        在主线程运行(会阻塞UI)
      </button>
      
      <div class="input-group">
        <label>计算次数:</label>
        <input v-model.number="iterations" type="number" min="1000" max="10000000">
      </div>
    </div>

    <div class="results">
      <div class="result-card">
        <h3>Worker 线程结果</h3>
        <div v-if="workerResult">
          <p>计算结果: {{ workerResult.result }}</p>
          <p>耗时: {{ workerResult.duration }}ms</p>
          <p>状态: {{ workerStatus }}</p>
        </div>
        <div v-else>
          <p>等待 Worker 返回结果...</p>
        </div>
      </div>

      <div class="result-card">
        <h3>主线程结果</h3>
        <div v-if="mainThreadResult">
          <p>计算结果: {{ mainThreadResult.result }}</p>
          <p>耗时: {{ mainThreadResult.duration }}ms</p>
          <p>UI阻塞: {{ mainThreadResult.blocked ? '是' : '否' }}</p>
        </div>
      </div>
    </div>

    <div class="logs">
      <h3>日志</h3>
      <div class="log-list">
        <div v-for="(log, index) in logs" :key="index" class="log-item">
          [{{ log.time }}] {{ log.message }}
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, onUnmounted } from 'vue'

// 响应式数据
const iterations = ref(1000000)
const workerRunning = ref(false)
const workerResult = ref(null)
const mainThreadResult = ref(null)
const logs = ref([])
const workerStatus = ref('未启动')

// Worker 实例
let worker = null

// 添加日志
const addLog = (message) => {
  logs.value.unshift({
    time: new Date().toLocaleTimeString(),
    message
  })
  if (logs.value.length > 20) logs.value.pop()
}

// 启动 Worker
const startWorker = () => {
  if (typeof Worker === 'undefined') {
    addLog('❌ 浏览器不支持 Web Workers')
    return
  }

  try {
    // 使用 Vite 的特殊导入方式
    worker = new Worker(new URL('../workers/calculator.worker.js', import.meta.url), {
      type: 'module'  // 如果需要 ES 模块
    })
    
    worker.onmessage = (event) => {
      workerResult.value = event.data
      workerStatus.value = '空闲'
      addLog(`✅ Worker 返回结果: ${event.data.result}`)
    }
    
    worker.onerror = (error) => {
      addLog(`❌ Worker 错误: ${error.message}`)
      workerStatus.value = '错误'
    }
    
    workerRunning.value = true
    workerStatus.value = '等待中'
    addLog('🚀 Worker 已启动')
  } catch (error) {
    addLog(`❌ 创建 Worker 失败: ${error.message}`)
  }
}

// 发送消息到 Worker
const sendMessage = () => {
  if (worker) {
    const data = {
      type: 'calculate',
      iterations: iterations.value,
      timestamp: Date.now()
    }
    worker.postMessage(data)
    workerResult.value = null
    workerStatus.value = '计算中'
    addLog(`📤 发送计算任务: ${iterations.value} 次迭代`)
  }
}

// 停止 Worker
const stopWorker = () => {
  if (worker) {
    worker.terminate()
    worker = null
    workerRunning.value = false
    workerStatus.value = '已停止'
    addLog('🛑 Worker 已停止')
  }
}

// 在主线程运行计算(会阻塞 UI)
const runInMainThread = () => {
  addLog('⚠️ 在主线程开始计算(UI会卡住)...')
  const startTime = performance.now()
  
  // 模拟耗时计算
  let result = 0
  for (let i = 0; i < iterations.value; i++) {
    result += Math.sqrt(i) * Math.sin(i)
    // 每 10000 次检查一次,模拟 UI 卡顿
    if (i % 10000 === 0) {
      // 这会阻塞 UI 更新
      mainThreadResult.value = {
        result: result.toFixed(2),
        duration: (performance.now() - startTime).toFixed(2),
        blocked: true
      }
    }
  }
  
  const duration = performance.now() - startTime
  mainThreadResult.value = {
    result: result.toFixed(2),
    duration: duration.toFixed(2),
    blocked: true
  }
  addLog(`⏱️ 主线程计算完成,耗时: ${duration.toFixed(2)}ms`)
}

// 组件卸载时清理
onUnmounted(() => {
  if (worker) {
    worker.terminate()
  }
})
</script>

<style scoped>
.worker-test {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}

.controls {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 30px;
  padding: 20px;
  background: #f5f5f5;
  border-radius: 8px;
}

.controls button {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  background: #007bff;
  color: white;
  cursor: pointer;
  transition: background 0.3s;
}

.controls button:hover:not(:disabled) {
  background: #0056b3;
}

.controls button:disabled {
  background: #ccc;
  cursor: not-allowed;
}

.input-group {
  display: flex;
  align-items: center;
  gap: 10px;
}

.input-group input {
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  width: 150px;
}

.results {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-bottom: 30px;
}

.result-card {
  padding: 20px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.result-card h3 {
  margin-top: 0;
  color: #333;
}

.logs {
  background: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
}

.log-list {
  max-height: 300px;
  overflow-y: auto;
}

.log-item {
  padding: 8px 0;
  border-bottom: 1px solid #eee;
  font-family: 'Courier New', monospace;
  font-size: 14px;
}
</style>

4. Worker 文件

src/workers/calculator.worker.js

javascript

javascript 复制代码
// Web Worker - 执行耗时计算

self.onmessage = function(event) {
  const { type, iterations, timestamp } = event.data
  
  if (type === 'calculate') {
    const startTime = performance.now()
    
    try {
      // 模拟复杂计算
      let result = heavyCalculation(iterations)
      
      const duration = performance.now() - startTime
      
      // 发送结果回主线程
      self.postMessage({
        type: 'result',
        iterations,
        result,
        duration,
        timestamp,
        workerId: self.name || 'calculator'
      })
    } catch (error) {
      self.postMessage({
        type: 'error',
        error: error.message,
        timestamp
      })
    }
  }
}

// 耗时计算函数
function heavyCalculation(iterations) {
  let sum = 0
  for (let i = 0; i < iterations; i++) {
    sum += Math.sqrt(i) * Math.sin(i) * Math.cos(i)
  }
  return sum
}

// 支持多种计算类型
function fibonacci(n) {
  if (n <= 1) return n
  return fibonacci(n - 1) + fibonacci(n - 2)
}

function primeCheck(num) {
  if (num <= 1) return false
  for (let i = 2; i <= Math.sqrt(num); i++) {
    if (num % i === 0) return false
  }
  return true
}

// 错误处理
self.onerror = function(error) {
  console.error('Worker error:', error)
}

5. 使用组合式函数封装 Worker

src/composables/useWorker.js

javascript

javascript 复制代码
import { ref, onUnmounted } from 'vue'

export function useWorker(workerPath, options = {}) {
  const worker = ref(null)
  const isRunning = ref(false)
  const result = ref(null)
  const error = ref(null)
  const isLoading = ref(false)

  const initWorker = () => {
    if (typeof Worker === 'undefined') {
      throw new Error('浏览器不支持 Web Workers')
    }

    try {
      worker.value = new Worker(new URL(workerPath, import.meta.url), options)
      isRunning.value = true
      
      return true
    } catch (err) {
      error.value = err.message
      return false
    }
  }

  const postMessage = (data) => {
    if (!worker.value) return
    
    return new Promise((resolve, reject) => {
      isLoading.value = true
      
      const messageHandler = (event) => {
        if (event.data.type === 'error') {
          error.value = event.data.error
          reject(event.data)
        } else {
          result.value = event.data
          resolve(event.data)
        }
        isLoading.value = false
        worker.value.removeEventListener('message', messageHandler)
      }
      
      const errorHandler = (err) => {
        error.value = err.message
        isLoading.value = false
        reject(err)
        worker.value.removeEventListener('error', errorHandler)
      }
      
      worker.value.addEventListener('message', messageHandler)
      worker.value.addEventListener('error', errorHandler)
      
      worker.value.postMessage(data)
    })
  }

  const terminate = () => {
    if (worker.value) {
      worker.value.terminate()
      worker.value = null
      isRunning.value = false
    }
  }

  // 自动清理
  onUnmounted(terminate)

  return {
    worker,
    isRunning,
    result,
    error,
    isLoading,
    initWorker,
    postMessage,
    terminate
  }
}

在组件中使用组合式函数

vue

javascript 复制代码
<template>
  <div>
    <button @click="calculate">使用组合式函数计算</button>
    <div v-if="workerResult">
      结果: {{ workerResult.result }}
    </div>
  </div>
</template>

<script setup>
import { useWorker } from '../composables/useWorker'

const {
  isRunning,
  result: workerResult,
  error,
  isLoading,
  initWorker,
  postMessage,
  terminate
} = useWorker('../workers/calculator.worker.js')

// 初始化 Worker
initWorker()

const calculate = async () => {
  try {
    await postMessage({
      type: 'calculate',
      iterations: 1000000
    })
  } catch (err) {
    console.error('计算失败:', err)
  }
}
</script>

6. Vite 配置(vite.config.js)

javascript

javascript 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  
  // Worker 配置
  worker: {
    format: 'es', // 使用 ES 模块
    plugins: []   // 可以添加 worker 专用插件
  },
  
  // 开发服务器配置
  server: {
    port: 3000,
    open: true, // 自动打开浏览器
    cors: true
  },
  
  // 构建配置
  build: {
    target: 'esnext',
    sourcemap: true
  }
})

7. 在 Vue Router 中测试

创建测试页面

vue

javascript 复制代码
<!-- src/views/WorkerTest.vue -->
<template>
  <div class="worker-test-view">
    <h1>Web Worker 性能对比</h1>
    <PerformanceComparison />
  </div>
</template>

<script setup>
import PerformanceComparison from '../components/PerformanceComparison.vue'
</script>

路由配置

javascript

javascript 复制代码
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import WorkerTest from '../views/WorkerTest.vue'

const routes = [
  {
    path: '/worker-test',
    name: 'WorkerTest',
    component: WorkerTest
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

8. 测试不同类型的数据传输

复杂 Worker - 支持多种操作

javascript

javascript 复制代码
// src/workers/multi-purpose.worker.js
self.onmessage = async function(e) {
  const { type, data, id } = e.data
  
  try {
    let result
    
    switch (type) {
      case 'image-process':
        result = await processImage(data)
        break
        
      case 'json-parse':
        result = JSON.parse(data)
        break
        
      case 'heavy-compute':
        result = heavyCompute(data)
        break
        
      case 'api-fetch':
        result = await fetchData(data)
        break
        
      default:
        throw new Error(`未知的操作类型: ${type}`)
    }
    
    self.postMessage({
      id,
      type,
      result,
      success: true
    })
  } catch (error) {
    self.postMessage({
      id,
      type,
      error: error.message,
      success: false
    })
  }
}

async function processImage(imageData) {
  // 模拟图像处理
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        processed: true,
        size: imageData.length,
        preview: 'data:image/png;base64,...'
      })
    }, 1000)
  })
}

function heavyCompute(params) {
  let result = 0
  for (let i = 0; i < params.iterations; i++) {
    result += Math.complexOperation(i)
  }
  return result
}

async function fetchData(url) {
  const response = await fetch(url)
  return response.json()
}

9. 快速启动脚本

创建 setup-vue-worker.sh

bash

bash 复制代码
#!/bin/bash

echo "🚀 创建 Vue 3 + Web Worker 测试项目..."

# 使用 Vite 创建项目
npm create vue@latest vue-worker-demo -- --typescript --router --pinia

cd vue-worker-demo

# 创建目录结构
mkdir -p src/workers src/composables src/components

# 创建 Worker 文件
cat > src/workers/demo.worker.js << 'EOF'
self.onmessage = function(e) {
  console.log('Worker received:', e.data)
  
  // 模拟耗时操作
  const start = performance.now()
  let result = 0
  for (let i = 0; i < e.data.iterations; i++) {
    result += Math.sqrt(i)
  }
  const duration = performance.now() - start
  
  self.postMessage({
    result: result.toFixed(2),
    duration: duration.toFixed(2),
    input: e.data
  })
}
EOF

# 创建测试组件
cat > src/components/WorkerDemo.vue << 'EOF'
<template>
  <div class="demo">
    <h2>Vue 3 + Web Worker Demo</h2>
    <button @click="runWorker">Run in Worker</button>
    <button @click="runMain">Run in Main Thread</button>
    
    <div v-if="result">
      <h3>Result: {{ result.result }}</h3>
      <p>Duration: {{ result.duration }}ms</p>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const result = ref(null)

const runWorker = () => {
  const worker = new Worker(new URL('../workers/demo.worker.js', import.meta.url))
  
  worker.onmessage = (e) => {
    result.value = e.data
    worker.terminate()
  }
  
  worker.postMessage({ iterations: 1000000 })
}

const runMain = () => {
  const start = performance.now()
  let res = 0
  for (let i = 0; i < 1000000; i++) {
    res += Math.sqrt(i)
  }
  const duration = performance.now() - start
  
  result.value = {
    result: res.toFixed(2),
    duration: duration.toFixed(2),
    input: { iterations: 1000000 }
  }
}
</script>
EOF

# 更新 App.vue
cat > src/App.vue << 'EOF'
<template>
  <WorkerDemo />
</template>

<script setup>
import WorkerDemo from './components/WorkerDemo.vue'
</script>
EOF

# 安装依赖并启动
npm install
npm run dev

echo "✅ 项目创建完成!"
echo "🌐 访问: http://localhost:5173"

运行:

bash

bash 复制代码
chmod +x setup-vue-worker.sh
./setup-vue-worker.sh

10. 关键注意事项

  1. 路径问题 :Vue 项目中需要使用 new URL('./worker.js', import.meta.url)

  2. 模块类型 :如果需要 ES 模块,添加 { type: 'module' }

  3. 开发模式:Vite 在开发模式下会自动处理 Worker

  4. 生产构建:构建时会单独打包 Worker 文件

  5. HMR 支持:Worker 修改后可能需要手动刷新

  6. 类型支持:如果使用 TypeScript,需要添加类型声明


11. TypeScript 支持

typescript

TypeScript 复制代码
// src/workers/types.ts
export interface WorkerMessage {
  type: string
  data?: any
  id?: string
}

export interface WorkerResult {
  id?: string
  type: string
  result?: any
  error?: string
  success: boolean
}

// src/workers/typed.worker.ts
import type { WorkerMessage, WorkerResult } from './types'

const ctx: Worker = self as any

ctx.onmessage = function(e: MessageEvent<WorkerMessage>) {
  // TypeScript 类型安全
  const result: WorkerResult = {
    id: e.data.id,
    type: e.data.type,
    result: 'processed',
    success: true
  }
  ctx.postMessage(result)
}

这样你就可以在 Vue 3 中全面测试工作者线程了!

相关推荐
春生野草7 小时前
Ruoyi前端基于vue的脚手架的目录解析
前端·javascript·vue.js
程序修理员7 小时前
java+vue实现文件下载进度条
java·开发语言·vue.js
ohyeah8 小时前
使用 Vue 3 实现大模型流式输出:从零搭建一个简易对话 Demo
前端·vue.js·openai
Rysxt_8 小时前
Vue文件下载功能完整指南:从基础实现到进阶实战
前端·javascript·vue.js
一 乐9 小时前
智慧养老|基于springboot+小程序社区养老保障系统设计与实现(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端·小程序
老华带你飞9 小时前
旅游|基于Java旅游信息推荐系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端·旅游
老华带你飞10 小时前
医院挂号|基于Java医院挂号管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot
yzx99101310 小时前
基于Flask+Vue.js的智能社区垃圾分类管理系统 - 三创赛参赛项目全栈开发指南
vue.js·分类·flask
+VX:Fegn089511 小时前
人力资源管理|基于springboot + vue人力资源管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计