1. 什么是Web Worker
- Web Worker是一种在浏览器中运行的JavaScript线程,能够独立于主线程运行。
- Web Worker可以加速Web应用的响应速度和处理能力。
2. Web Worker的使用场景
- 处理大量计算密集型任务,如图像处理、音视频编码、多线程下载等。
- 后台执行长时间运行的任务,如复杂的数据处理和分析。
3. 如何使用Web Worker
3.1 方法说明
- 开启worker
// Worker 构造函数
const worker = new Worker('worker.js')
- 监听数据
// 写法 1
self.addEventListener('message', function (e) {})
// 写法 2
this.addEventListener('message', function (e) {})
// 写法 3
addEventListener('message', function (e) {})
// 写法 4
onmessage = function (e) {}
3. 错误监听
// 主线程
worker.onerror = function () {}
// 主线程使用专用线程
worker.onmessageerror = function () {}
// 主线程使用共享线程
worker.port.onmessageerror = function () {}
// worker 线程
onerror = function () {}
4. 数据传递
// 主线程
worker.postMessage([10, 24])
// 子线程
postMessage(data)
5. 关闭worker
// 主线程
worker.terminate()
// Dedicated Worker 线程中
self.close()
3.2 原生使用案例
使用vue3进行演示,当表格数据很大的情况,数据处理速度会慢,定义getList函数来获取数据。在getList中,将数据传递给handleWorker函数进行处理。
handleWorker函数中:
- 首先判断浏览器是否支持Web Worker,如果支持,则创建一个Worker,并向Worker发送数据。
- Worker会在后台进行数据处理,并将处理结果返回给主线程。
- 主线程可以通过onmessage事件监听Worker返回的消息,并将消息中包含的数据赋值给tableData变量。这样,只要tableData变量发生变化,Vue组件就会自动更新视图。
在vue文件中创建Webworker.vue,将worker.js放在public下
Webworker.vue:
<template>
<div>
<el-table border :data="tableData" style="width: 100%; margin-top: 30px">
<el-table-column prop="id" label="id" />
<el-table-column prop="name" label="名字" />
<el-table-column prop="time" label="时间" />
<el-table-column prop="date" label="日期" />
</el-table>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
const tableData =ref<any[]>([])
const getList = async () => {
const res = await fetch('http://localhost:5173/main/getList').then(res => res.json())
handleWorker(res.data)
}
const handleWorker = (data) => {
if (window.Worker) {
console.log('存在worker');
let worker = new Worker('worker.js')
worker.postMessage(data)
worker.onmessage = function (e) {
console.log('打印***接受worker的信息', e.data)
tableData.value = e.data
}
worker.onmessageerror = function (err) {
console.log('error',err);
}
} else {
console.log('没有worker');
}
}
onMounted(()=>{
getList()
})
</script>
worker.js
onmessage = function (e) {
console.log("打印***接受主进程信息", e.data);
console.time('worker')
const handleData = e.data.map(item => {
if (item.id !== 7) {
return item
}
item.sex = 'man'
item.pid = item.id
item.key = item.id
return item
})
console.timeEnd("worker");
postMessage(handleData)
self.close()
};
3.3 使用Vueuse中useWebWorker、useWebWorkerFn
将上面的代码进行改造,
- useWebWorker将handleWorker使用post进行替换,数据返回使用watch进行监听即可
- useWebWorkerFn更加高级,它将Web Worker的实现和数据处理的过程都封装起来,只提供一个数据处理函数
<template>
<div>
<el-table border :data="tableData" style="width: 100%; margin-top: 30px">
<el-table-column prop="id" label="id" />
<el-table-column prop="name" label="名字" />
<el-table-column prop="time" label="时间" />
<el-table-column prop="date" label="日期" />
</el-table>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useWebWorkerFn,useWebWorker } from '@vueuse/core'
// =======================useWebWorkerFn==============================
/** 大型数组(500 万个数字)的排序 */
function heavyTask() {
const randomNumber = () => Math.trunc(Math.random() * 5_000_00)
const numbers: number[] = Array(5_000_000).fill(undefined).map(randomNumber)
console.log('打印***numbers',numbers)
numbers.sort()
return numbers.slice(0, 5)
}
/**
* workerTerminate(status) 'PENDING' | 'SUCCESS' | 'RUNNING' | 'ERROR' | 'TIMEOUT_EXPIRED'
* workerStatus 状态
* workerFn
*/
const { workerFn, workerStatus, workerTerminate } = useWebWorkerFn(heavyTask)
onMounted(async ()=>{
// res 为heavyTask的返回值
const res = await workerFn()
workerTerminate('PENDING') // 关闭
})
// =======================useWebWorker==============================
const {data,post,terminate,worker} = useWebWorker('worker.js')
watch(() => data.value, (newData) => {
console.log(newData, 'newData');
terminate()
})
const tableData =ref<any[]>([])
const getList = async () => {
const res = await fetch('http://localhost:5173/main/getList').then(res => res.json())
post(res.data)
}
onMounted(()=>{
getList()
})
</script>
4. 总结
Web Worker的限制和注意事项
- Web Worker不能直接访问DOM API,需要使用postMessage()方法和主线程进行通信。
- 利用Web Worker可能增加页面资源消耗,需要合理使用。
- 在某些浏览器中,Web Worker可能不支持所有ES6语法。
参考文章