这次好好聊聊如何借助DevTools来进行前端的性能分析,这也是我找了很久,非常想了解的使用技巧! 应该算是DevTools的进阶篇吧~

记忆中倒是看到过有前辈写过这方面的文章,不过总是看的不够过瘾,一部分原因是因为可能当时对前端的了解并不够充分,还可能是因为那些文章省去了一些我觉得比较重要的知识点,导致读者看完以后并没能很好的顿悟!

当然,我也不敢保证这篇文章一定会让你有所收获,因为那和我写的主要目的并没有多大交集!呵呵~~

废话不多说了,下面的内容来自官方手册,英语好极了的童鞋可以直接跳转f这里~~

Timeline面板中记录和分析了你的应用在运行时所有的活动情况,它是你排查应用性能瓶颈的最佳工具!

Timeline面板预览

Timeline面板有三个主要的窗口:顶部的预览窗口,中间的记录窗口,底部的工具条,如下图:

上面的一些按钮单独解释真的很吃力,但其实在实战中使用时,很快就能掌握它们的作用!

在你开启记录(就好像打开了录像机的录制功能)后,在这期间你的应用中发生的所有事件都会被记录下来形成一条日志记录,按照记录的类型一般会标注上不同的颜色:

举个例子,下面的截图中显示了一个html页面的事件日志记录,可以看到,第一条记录表示的是浏览器为了获取html页面而发送了http请求的发送请求事件,接下来是一个接受响应事件,和接受对应响应体(html页面内容的字节)的事件,最后是一个加载完毕事件记录。

除了Records视图外,你还可以在三个模式中切换关注点:

  • Events:显示所有的事件记录
  • Frames:显示页面渲染的帧数
  • Memory:显示页面的内存使用情况

我们来一个一个介绍~

Events 模式

Events模式提供了一个概览图,包含了在记录期间你的页面被捕获到的所有事件,你可以很容易的看到你的应用到底哪里耗时最久,等等~~

每个带颜色的横条表示特定类型的事件,横条的长度代表的是该事件从开始到结束的耗时~~

你可以在Events视图的时间轴中可以截取某一个时间段,来避免太多非关注事件日志的视觉干扰~~

Frames 模式

帧模式从渲染性能的角度提供了数据支撑,一个“frame”表示浏览器为了渲染单个内容块而必须要做的工作(包括:执行js,处理事件,修改DOM,更改样式和布局,绘制页面)!

我们的目标是保证我们的页面要有高于每秒60帧的跑分,这和目前大多数显示器的刷新率相吻合(60Hz)!
这么算的话,我们的应用的每一帧耗时应该低于16.6ms(1000ms/60)~~

上图中,在Frame视图中有两条贯穿该视图的横线,分别标识出60FPS和30FPS的基准,按照我们刚才提到的那个公式,我们可以理解为分别标识了16.6ms和33.3ms两个时间点~~
图中帧柱的高度表示了该帧的总耗时,帧柱中的颜色分别对应该帧中包含的不停类型的事件!

注意上图中的鼠标位置,该工具条显示了当前选中的帧柱的耗时,可以看到鼠标指上去后会显示出该帧柱的详细数据!

你可能注意到了在帧柱上存在灰色区域和空白区域,它们分别代表:

  • 灰色区块:那些没有被DevTools感知到的活动
  • 空白区块:显示刷新周期(display refresh cycles)中的空闲时间段

如下图所示:

当你在Frame视图中选择了一个区域,在Timeline面板的最底部(右下方)会统计出你选中的时间范围内的帧柱的统计数据,如下图:

当鼠标指上去后会弹出详细信息,包含下面字段:

  • Selected range:选中的时间段内包含的帧柱数
  • Minimum Time:该时间段里帧柱中的最小耗时
  • Average Time:该时间段里帧柱的平均耗时
  • Maximum Time:该时间段里帧柱中的最大耗时
  • Standard Deviation:计算平均耗时的有效偏差值
  • Time by category:耗时的分布情况

Memory 模式

Memory视图显示了我们的应用的内存使用情况图表,包括页面的文档数,DOM节点数,和没有被垃圾回收掉的仍然在内存中的事件监听器数,如下图:

来一发

我们来一次模拟一次流程吧~

  1. 打开一个你想分析的页面;
  2. 打开DevTools的Timeline面板,点击”Record”按钮,或者使用热键:Ctrl + E(Win), Cmd + E(Mac);
  3. 然后在页面上操作你想要了解的功能;
  4. 停止记录,分析收集到的记录数据。

收集记录的小技巧

  • 尽可能的缩短记录的时间:确保记录的时间段很短,有利于锁定关注点,避免太多不必要的数据干扰;
  • 避免不必要的操作:同上道理,在记录期间,尽可能只触发你想分析的操作;
  • 关闭浏览器缓存:如果你想分析网络操作细节,你最好关闭浏览器缓存;
  • 关闭浏览器扩展工具:这样可以避免记录到这些第三方插件产生的行为记录,你可以开启浏览器的“隐身模式”~

锁定强制同步布局瓶颈

所谓布局(Layout),指的是浏览器计算页面中所有元素的位置和尺寸的过程!通常我们的Chrome会延迟布局计算,这样可以攒够一批变更(css变化或dom结构变化)后一次性执行!但是我们的脚本可以强制浏览器立刻进行布局操作,频繁的进行强制同步布局很可能会造成性能瓶颈!

Timeline面板会用“黄色惊叹号三角标”标注出那些发生的强制浏览器布局,当你查看对应记录详情时,也会在弹出的窗口中看到问题代码的调用堆栈信息,如下:

如果一个记录包含子记录触发了强制布局,那父记录会被一个“浅黄色惊叹号三角标”标识,如下图:

这种子记录是如何产生的呢?原因有二:

  1. 在执行某个事件的过程中同步产生了其他事件;
  2. 当打开了底部工具条中的“Glue asynchronous events to causes”开关后,异步事件记录会作为触发该异步事件的记录的子记录。

下面分别举两个例子:

当Chrome尝试去解析一段HTML时,如果发现该HTML引用了一些外部资源,那么Chrome必须先加载这些文件才能完成解析,这就是上面说的第一点,如下图:

在前端浏览器中存在很多异步回调的场景,比方说ajax。由于触发异步和异步回调事件之间可能间隔很长时间,这对于我们分析数据造成了一定程度的不便,所以我们可以打开“Glue asynchronous events to causes”开关,如下图:

心细的童鞋注意到了在上图中展示的第一条记录条中,被标识了不同颜色区块~它们分别对应下面的含义:

  • 左边深色:表示父记录和它的同步子记录的耗时(长度);
  • 中间稍浅:表示父记录和它的异步子记录的耗时;
  • 右边泛白:表示从第一个异步事件开始到最后一个异步事件结束的耗时。

如下图:

当鼠标指向某个记录时,弹出的窗口中显示了该记录详细的数据:

  • Duration:匹配该记录及其子记录的总耗时;
  • Self Time:只包含自己得耗时(不包含子记录);
  • CPU time:CPU耗时。

如下图:

Timeline中的各种事件

最后我们再来看一下前面提到的各种事件类型:

Loading事件

事件 描述
Parse HTML 浏览器执行HTML解析
Finish Loading 网络请求完毕事件
Receive Data 请求的响应数据到达事件,如果响应数据很大(拆包),可能会多次触发该事件
Receive Response 响应头报文到达时触发
Send Request 发送网络请求时触发

Scripting事件

事件 描述
Animation Frame Fired 一个定义好的动画帧发生并开始回调处理时触发
Cancel Animation Frame 取消一个动画帧时触发
GC Event 垃圾回收时触发
DOMContentLoaded 当页面中的DOM内容加载并解析完毕时触发
Evaluate Script A script was evaluated.
Event js事件类型
Function Call 只有当浏览器进入到js引擎中时触发
Install Timer 创建计时器(调用setTimeout()和setInterval())时触发
Request Animation Frame A requestAnimationFrame() call scheduled a new frame
Remove Timer 当清除一个计时器时触发
Time 调用console.time()触发
Time End 调用console.timeEnd()触发
Timer Fired 定时器激活回调后触发
XHR Ready State Change 当一个异步请求为就绪状态后触发
XHR Load 当一个异步请求完成加载后触发

Rendering事件

事件 描述
Invalidate layout 当DOM更改导致页面布局失效时触发
Layout 页面布局计算执行时触发
Recalculate style Chrome重新计算元素样式时触发
Scroll 内嵌的视窗滚动时触发

Painting事件

事件 描述
Composite Layers Chrome的渲染引擎完成图片层合并时触发
Image Decode 一个图片资源完成解码后触发
Image Resize 一个图片被修改尺寸后触发
Paint 合并后的层被绘制到对应显示区域后触发

那么关于Timeline面板的大致介绍就到此为止吧。