共计 2410 个字符,预计需要花费 7 分钟才能阅读完成。
这篇文章将介绍下实际使用 performance 对页面进行优化的过程。总的来说,chrome performance 工具让我们更方便的发现在代码运行过程中的问题在哪里,便于对一些可能注意不到的问题进行定位、分析和优化。原文首发于个人博客
渲染优化
首先,我们对进入整个详情页进行分析,整个页面的结构大致如下,主要包含三个部分:基本信息,可视化图和时间轴。时间轴内部包含时间轴的详情信息,包括表格和几种类型的可视化图等,内部比较重。如图所示:
我们点击录制,查看进入页面的性能图:
优化点 1
可以看到,渲染当前页面的耗时将近 1.2s,我们看看到底是哪里出现了问题。对渲染部分进行放大,我们发现在渲染时间轴的过程中,大部分时间耗费在了每个时间节点详情内容的展示上面,但是默认状态下,他们是进行隐藏的。也就是说,我们进行了 N 个节点的不必要的渲染,只需把他们干掉就好了。
查看代码,发现是以下位置导致的,我们进行了一个展开盒子的封装,类似这样:
<Box data={data} border collapse loading={loading}>
<AlertDetail />
</Box>
在非展开状态下,我们依然对内容进行了渲染,只是使用样式进行了隐藏,但是这样 React 仍然会进行虚拟 dom 的渲染和计算,在这里我们对其进行改造,让其只在展开时进行渲染,并尽量缓存渲染的结果。修改完成后,我们会发现,Scripting 由之前的 880+ms 变成了 670ms 减少了 200ms 左右:
优化点 2
我们继续看,会发现页面初始化时,详情部分会有大量的 Evaluate Script 耗时,主要是耗费在告警详情右侧的分类及各分类下的可视图及详情引起的。
我们可能会想,这里在初始化时并没有进行渲染,为什么仍然会耗费时长进行计算呢?继续追查我发现原因在这里:
在整个详情组件中,我们对包括 http,tcp 等所有类型的组件进行了引用,然后根据其类型进行组件的匹配,在各个组件中可能包含了每个类型对应的定义、分类和计算等等等等,不仅增加了加载时间,更延长了初始化时间,显然我们这么做是不对的。
在此,我们可以使用懒加载方式对其进行优化,仅展示其对应类型的图,避免了不必要的资源浪费和计算时间。
修改之后,我们再次进行性能测试,发现在进入页面时,详情组件的耗费时长由 260ms 变为不到 2ms:
而 Scripting 由之前的 670+ms 变成了 415ms 减少了 250ms 左右:
至此,进入页面的耗时已由最开始的 1.2s 左右变成了现在的 0.7s 左右。
更新优化
我们在点击时间轴查看详情时,会进行几个操作。关闭其他已开启的详情内容,展开当前详情内容,根据当前的类型进行对应类型的详情内容展示,上边已经提到过。这个过程会涉及到 React 的 update 操作,我们来对这个过程进行一下优化。
优化点 1
首先我们点击录制按钮,然后点击展开,并对其进行录制。我们会发现以下的结果:
点击展开按钮,Timeline 组件进行了多次重复渲染,显然这是不应该的,我们来看下是哪里导致的。我们看到整个时间轴组件中,有这样一段代码,在时间轴组件中使用 connect 连接了 timeline 和 alertList 两个数据,其中,alertList 数据是详情内种中对应的告警列表。两组数据对应的更改都会映射到组件的更新,比如时间轴的展开收起,以及 alertList 请求,请求成功及失败等。
按理来说,alertList 对应的请求,仅对应到当前展开内容的更新即可。因此,我们对此有几种修改方案:
时间轴组件中弃用对 alertList 的引用,以保证 alertList 不会牵连到时间轴组件整体更新
将时间轴的渲染和详情渲染进行分离,向其传递各自对应的数据,通过 PureComponent 来控制更新
使用 shouldComponentUpdate 进行优化
在此我们采用第一种,避免 alertList 对整个组件的影响。
我们会发现,点开详情后,整个 timeLine 只进行了一次大更新,详情的更新只在展开的组件中进行。
优化点 2
我们会发现,在对某一个时间点进行展开时,整个 list 列表的节点都进行了更新,如下图所示。显然这样的性能耗费是及其大且不重要的。假如列表的内容很多,那极有可能造成大量的更新导致页面卡死。
因为其他的 list 列表节点都引用了 alertList 这个 prop, 当其进行改变时,所有的节点都会进行更新,所以我们需要使用 shouldComponentUpdate 手动对其进行优化:
// 当开关状态变化时,才从新渲染,否则不需要
shouldComponentUpdate (nextProps, nextState) {
const opened = _.get(this.props, ‘open’)
const willOpen = _.get(nextProps, ‘open’)
if (opened === willOpen && !willOpen) {
return false
} else {
// 类似 pureComponent 进行浅比较
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState)
}
}
再次进行录制,我们发现只要当前 Item 进行了更新,整个 Timeline 更新时间缩短到不到 40ms,极大的增加了体验。
其他优化过程与上面类似,不再赘述。
总结
通过配合 performance 工具进行一步步优化,整个页面的渲染和更新性能有了极大的提升,我们也借此知道了在平时书写代码过程中需要注意的问题。我们简单的做一下总结:
避免非展示状态的不必要的渲染
必要时,手动进行懒加载以避免大型模块对页面进行营销,避免加载不必要的模块
保证展示组件 props 的纯净性,避免因其他 props 的更改导致组件进行更新
必要时,可使用 shouldComponent 进行手动优化
平时可通过 PureComponent 来避免不必要的组件更新