@author: 郭瑞峰 @createTime: 2023/08/31 @updateTime: 2023/09/03
前言
之前支援其他组时候遇见过一个很难受的卡顿问题
因为后端给了几十万组数据,要前端经过多次复杂算法算出结果后展示在 矩形树图 中,运算量大到能把标签页卡顿20秒。
还好之前了解过web-worker
,所以说我就想到用web-worker
优化卡顿。
先放demo
把,大家可以先去感受感受把,若没有任何卡顿,恭喜你了,你用的CPU性能超群(体验就不会这么明显)。
项目地址:web-worker demo
线上demo: 线上demo
web-worker 原理
简单来说,页面需要大量运算,运算需要一定时间时,可以让主线程
申请一个Worker
线程,由Worker
线程来处理大量运算,运算完成后返回给主线程
,这样不会造成主线程
的卡顿问题。
详细来说的话浏览器每个页面(标签页)都会给一个线程,这个线程会用于 html
、js
、css
一系列加载渲染,当加载js
时候会暂停后续标签运行(若script
设置async
,这个就不会阻塞后续运行),这个时候如果js
代码出现庞大运算,计算消耗时间过长就会影响后续响应,为了不影响后续标签或其他事件响应,就需要一个手段让计算操作在其他线程中运行。
(严格来说,浏览器每个标签页都会分配至少一个进程,为了防止单个页签崩溃后影响其他页签,之后就是在当前进程中分配线程,详情请见 掘金大佬归纳)
web-worker 入手
web-worker
分为两部分,主线程
和Worker
线程,故因此会有主线程
引入注册使用销毁Worker
线程的一套流程,下面就是一个简单的demo
来实现web-worker
。
当然,你也可以结合我的demo
来看。
主线程注册使用销毁 worker线程
- react 使用示例
typescript
import React, { useState, useEffect } from 'react'
const Havefun: React.FC = () => {
const [worker, setWorker] = useState<Worker | null>()
useEffect(() => {
// 初始化worker线程,必须通过这种方法引入worker.js文件
setWorker(new Worker(
new URL('XXX.worker.js', import.meta.url),
{ type: 'module' }
))
// 如果使用next框架的话,请注意区分node和browser环境,下面是next的例子
/*
setWorker(typeof window !== 'undefined' && window.Worker ?
new Worker(
new URL('XXX.worker.js', import.meta.url),
{ type: 'module' }
) :
null)
*/
return () => {
// 销毁事件
worker?.removeEventListener('message', workerListener)
// 销毁worker线程
worker?.terminate()
}
}, [])
// worker 返回事件,便于注册和销毁
const workerListener = ({ data }: any) => {
console.log(data)
}
const useWorker = () => {
worker?.addEventListener('message', workerListener)
worker?.postMessage('hello, this is main thread')
}
return (<button onClick={useWorker}>call web-worker</button>)
}
Havefun.displayName = 'Havefun'
export default Havefun
- vue
html
<script setup lang="ts">
import { onUnmounted } from 'vue'
const worker: Worker | null = new Worker(
new URL('XXX.worker.js', import.meta.url),
{ type: 'module' }
)
// 如果使用nuxt框架的话,请注意区分node和browser环境,下面是nuxt的例子
/*
const worker = typeof window !== 'undefined' && window.Worker ?
new Worker(
new URL('XXX.worker.js', import.meta.url),
{ type: 'module' }
) :
null
*/
worker?.addEventListener('message', ({ data }) => {
console.log(data)
})
onUnmounted(() => {
worker?.terminate()
})
const useWorker = () => {
worker?.postMessage('hello, this is main thread')
</script>
<template>
<button @click="useWorker">call web-worker</button>
</template>
worker文件编写
javascript
// 注意:该文件是直接进入浏览器运行的,建议使用javascript而不是typescript
// test.worker.js
self.addEventListener('message', ({ data }) => {
console.log(data)
self.postMessage('hello, this is worker thread')
})
运行示例
我用的是next框架测试
web-worker应用
-
webgl
或者ffmpeg
这种2D3D图像渲染、视频处理需要处理运算大量数据 -
页面因为一些超级大的运算引起的卡顿
如何判断页面是否需要web-worker
在开发工具中找到performance
,这个是前端性能优化工具,操作前记录 ,操作完成后停止,等待片刻就会生成性能报告。
如果生成的报告中scripting
时间超过3秒,并且页面域很严重卡顿现象 ,建议使用web-worker
来缓解卡顿、主线程运算压力。