37

这篇文章将介绍下实际使用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耗时,主要是耗费在告警详情右侧的分类及各分类下的可视图及详情引起的。

clipboard.png

我们可能会想,这里在初始化时并没有进行渲染,为什么仍然会耗费时长进行计算呢?继续追查我发现原因在这里:

clipboard.png

在整个详情组件中,我们对包括http,tcp等所有类型的组件进行了引用,然后根据其类型进行组件的匹配,在各个组件中可能包含了每个类型对应的定义、分类和计算等等等等,不仅增加了加载时间,更延长了初始化时间,显然我们这么做是不对的。

在此,我们可以使用懒加载方式对其进行优化,仅展示其对应类型的图,避免了不必要的资源浪费和计算时间。

clipboard.png

修改之后,我们再次进行性能测试,发现在进入页面时,详情组件的耗费时长由260ms变为不到2ms:

clipboard.png

而Scripting由之前的670+ms变成了415ms减少了250ms左右:

clipboard.png

至此,进入页面的耗时已由最开始的1.2s左右变成了现在的0.7s左右。

更新优化

我们在点击时间轴查看详情时,会进行几个操作。关闭其他已开启的详情内容,展开当前详情内容,根据当前的类型进行对应类型的详情内容展示,上边已经提到过。这个过程会涉及到React的update操作,我们来对这个过程进行一下优化。

优化点1

首先我们点击录制按钮,然后点击展开,并对其进行录制。我们会发现以下的结果:

clipboard.png

点击展开按钮,Timeline组件进行了多次重复渲染,显然这是不应该的,我们来看下是哪里导致的。我们看到整个时间轴组件中,有这样一段代码,在时间轴组件中使用connect连接了timeline和alertList两个数据,其中,alertList数据是详情内种中对应的告警列表。两组数据对应的更改都会映射到组件的更新,比如时间轴的展开收起,以及alertList请求,请求成功及失败等。

clipboard.png

按理来说,alertList对应的请求,仅对应到当前展开内容的更新即可。因此,我们对此有几种修改方案:

  1. 时间轴组件中弃用对alertList的引用,以保证alertList不会牵连到时间轴组件整体更新
  2. 将时间轴的渲染和详情渲染进行分离,向其传递各自对应的数据,通过PureComponent来控制更新
  3. 使用shouldComponentUpdate进行优化

在此我们采用第一种,避免alertList对整个组件的影响。

我们会发现,点开详情后,整个timeLine只进行了一次大更新,详情的更新只在展开的组件中进行。

clipboard.png

优化点2

我们会发现,在对某一个时间点进行展开时,整个list列表的节点都进行了更新,如下图所示。显然这样的性能耗费是及其大且不重要的。假如列表的内容很多,那极有可能造成大量的更新导致页面卡死。

clipboard.png

因为其他的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,极大的增加了体验。

clipboard.png

其他优化过程与上面类似,不再赘述。

总结

通过配合performance工具进行一步步优化,整个页面的渲染和更新性能有了极大的提升,我们也借此知道了在平时书写代码过程中需要注意的问题。我们简单的做一下总结:

  1. 避免非展示状态的不必要的渲染
  2. 必要时,手动进行懒加载以避免大型模块对页面进行营销,避免加载不必要的模块
  3. 保证展示组件props的纯净性,避免因其他props的更改导致组件进行更新
  4. 必要时,可使用shouldComponent进行手动优化
  5. 平时可通过PureComponent来避免不必要的组件更新

Athon
1.7k 声望1.1k 粉丝