likes
comments
collection
share

记一次前端性能调优——火焰图实践

作者站长头像
站长
· 阅读数 15

问题记录

Antd 树组件中,通过点击向后端接口请求数据,最后渲染在页面的过程十分漫长,产品体验不佳。

分析过程

查看接口调用,返回数据条目 1w+ ,耗时 600 ms 。但是实际从请求开始到渲染出树组件耗时大约是 6s 。猜测是前端渲染过程使用大量资源和时间,最终使其呈现在页面上的过程很漫长。

到了这一步,如何优化? 记一次前端性能调优——火焰图实践

直接看代码根本看不出结果,这个渲染是使用 Antd 树组件进行的,这个时候通常我们就要甩锅给 antd 组件库了(谢谢蚂蚁大佬帮我背锅😄)。

但是甩锅给组件库解决不了问题。正常情况下, 1w+ 的数目远远达不到需要 6s 的加载时间这种情况。因此我们需要了解此次渲染的代码执行过程。这就需要祭出浏览器的 performance 工具了

火焰图实践

  1. 很简单,点击白点就可以进行录制了。

记一次前端性能调优——火焰图实践   2. 进行需要观测的操作后,点击结束按钮 stop

记一次前端性能调优——火焰图实践

  1. 最后我们可以通过生成的图片进行分析。

记一次前端性能调优——火焰图实践

可以看到,在 Network 一栏中此接口请求了一个node 接口,这个接口很快就返回了。

main 一栏中可以看到,请求接口后进行了一个 Task ,这就是 js 中常说的宏任务了,这个宏任务下主要是执行 microtasks ,就是所谓的微任务。这个微任务由一个匿名函 anonymouse 组成,这下我们清楚了,那么这个匿名函数是什么?

Xtset 组成,点击 set 任务,就可以看到整个 set 是哪里调用的。可以看到这个set是 mobx 库的函数,大致这个任务是 mobx 在执行数据标记。

记一次前端性能调优——火焰图实践

同时在开发环境下点击 anonymouse 函数,可以很清楚的看到代码中是哪里调用的此次微任务。

记一次前端性能调优——火焰图实践

现在大致清楚了,是一个微任务中的使用 mobx 相关代码造成了这次前端性能瓶颈。

  1. 现在我们需要把目标精确到某个语句。使用 perfermance.now()
//...
const t0 = perfermance.now()
store.tree = data
const t1 = perfermance.now()
console.log(t1 -t0) // 60000
//...

最后通过定位将此处代码找出来了。就是使用 mobx 定义的观察者,一瞬间接纳大量数据造成的。

发现是使用 mobx 造成的性能瓶颈,立刻就有三个方向可以进行了:

  • 优化 mobx 性能。
  • 替换使用的技术栈。
  • 替换状态管理方式。

根据 mobx 官网上的优化条例,好像不太适合在这个场景下,返回的树组件是一整个数据结构,我们无法将其进行拆分。可能能进行延迟设置,比如精确找到更新的数据,然后赋值。但这样做的成本太高了,果断放弃。

替换使用的技术栈。目前好像就是 reduxredux 在性能上和 mobx 比较没有很大的优势,而且一个项目也不允许有两种状态管理方式,果断放弃。

因此我们只能替换状态管理方式。那用什么状态管理方式,在 react 很明显是使用 react hooks 。即使用useState()

//...
cosnt [tree, setTree] = React.useState([])
//...
// store.tree = data
setTree(data)
//...
//...

优化结果

最后的结果也是不负众望,渲染效率提升了三倍,同一个接口返回后,渲染时间从 6s 缩短至 2s

记一次前端性能调优——火焰图实践

总结

  1. 性能优化只是背诵各种网上的优化手段只能是应试的。最终在生产中可以通过掌握火焰图来灵活地优化前端性能。然后通过perfermance.now() 精确到性能消耗最大的语句中。可以很明确地找到需要优化的语句。
  2. mobx 在渲染数据时会给每条数据打上跟踪标记,为数据变化时渲染做预备,在数据量大的情况下需要消耗大量资源,这个时候可以将 mobx 替换成 hooks state 作为性能优化手段之一。