前端开发如何排查性能瓶颈?并利用Web Worker优化海量数据计算
背景
在业务需求,有一个需求是需要对两个树形的数据结构进行深层次的对比。这个地方类似Vue或者React的Diff算法,当有大量节点,比如成千上万,或者上百万、千万、甚至上亿的数据时。这个计算会阻塞主线程,导致页面卡顿甚至卡死。用户无法操作页面,造成了不好的影响。
知识点
- JavaScript单线程执行: 浏览器中的JavaScript引擎是单线程运行,同一时刻只能执行一个任务。这意味着在执行复杂计算、大量数据处理等耗时操作时,主线程会暂时无法处理其他任务,包括UI渲染、事件处理、用户交互等。
- 事件循环与任务队列: 浏览器通过事件循环机制来协调各个任务的执行,主要包括宏任务队列(如script、setTimeout、setInterval、I/O、UI渲染等)和微任务队列(如Promise、MutationObserver等)。当主线程正在进行耗时计算时,即使有高优先级的渲染任务也无法立即执行,必须等到当前任务完成。
- UI渲染与JavaScript执行互斥: UI渲染发生在事件循环的特定阶段,当JavaScript正在进行大量计算时,浏览器无法进行正常的UI渲染更新。这就导致页面看起来像是“卡死”,无法及时响应用户的操作或展示新的内容。
- 大数据量对比的性能瓶颈: 对于大规模数据结构(如树形结构)进行深层次的对比计算,需要遍历、比较和处理大量数据,这些计算任务非常耗费CPU资源,占用时间长,严重阻碍了主线程执行其他任务的能力。
排查
我们可以利用Chrome-devtools自带的Performance便签,对所在页面进行排查。接下来我给大家简要介绍一下Performance标签页的排查过程。
1、我们首先打开Performance标签页,点击录制,然后执行你当前页面复杂的计算操作。
2、当执行完毕后,查看耗时操作
比如网络请求耗时
再比如JS耗时,我们可以在Call Tree中,看到当前页面的执行栈,是哪些方法较为耗时,然后在右边找到对应的代码段,找到对应的文件。最后按需进行优化
优化
这里主要讲的是业务逻辑中的JS代码如何优化。我在上述例子中,我发现我的页面卡顿,主要是我的JS对比逻辑耗时过长。然后由于我的是纯JS逻辑,不涉及DOM操作,于是我使用了Web Worker去优化。以下是代码实现:
首先我们新建一个worker.js,把复杂的耗时计算放到这个子线程内
function compareDiff(current,target){
...
}
self.onmessage = (e) => {
const { current, target } = e.data;
const data = compareDiff(current, target);
postMessage(data);
};
然后在主线程获取它计算完成后的数据
if (typeof Worker !== "undefined") {
const worker = new Worker(
import.meta.env.VITE_PUBLIC_API + "hooks/useCompare.js",
{type: "module"}
)
worker.addEventListener("message", (e) => {
// 接收消息
data.value = e.data;
});
worker.postMessage({
current,
target
}); // 向 worker 线程发送消息,对应 worker 线程中的 e.data
} else {
compareDiff(current, target);
}
提问:假设面对这样一个场景,在一个已采用懒加载方式加载到特定层级的el-tree
组件中,现在需要向该层级下的某个节点动态加载并插入大量子数据,并将其渲染至DOM结构中。鉴于此场景不适用于Web Worker技术进行优化,那么应该采取何种策略来避免因大规模数据渲染而导致的主线程阻塞及页面卡顿现象呢?
下一篇文章解答