我们都希望创建高性能的 Web 应用程序。由于我们的应用程序变得越来越复杂,我们可能想要支持丰富的画面以及理想的 60 帧/秒,这能保证我们的应用程序响应灵敏且生动流畅。
知道如何衡量和提高性能,是一个有用的技能,在这短短的文章中,我会带您简单回顾关于如何通过 Chrome DevTools 的 Timeline 和 Profiles 做到这一点。
记录
Timeline 工具栏提供了对于在装载你的 Web 应用的过程中,时间花费情况的概览,这些应用包括处理 DOM 事件、 页面布局渲染或者向屏幕绘制元素。
它可以让你深入得到三个模式的数据,来帮助你明白问什么你的应用很缓慢:事件、框架和实时的内存用量。开始浏览你的应用,并在 DevTools 中切换到 Timeline 工具栏。
默认情况下 Timeline 不会显示任何数据,但是你可以这样开始一个记录会话,打开你的应用并点击灰色圆圈☻,它在工具栏的底部——使用 Cmd/Ctrl + E 快捷键也能开始一个记录。
这个记录按钮会从灰色变成红色,而 Timeline 将开始从你的页面获取时间线(timeline)。在你的应用中完成一些操作,记录到一些数据之后,再一次点击按钮来停止记录。
请注意:
图标 | 说明 |
---|---|
会清除你现有的记录会话,以便开始一个新的会话。 | |
将会强迫 V8 完成一轮的垃圾回收。 | |
会过滤只显示耗时超过 15ms 的记录 |
检查
接下来,我们着手检查一下记录的数据。对影响性能的成本要素按优先级排序。是 JavaScript 吗?还是渲染?我们先看一看 Timeline Events 模式,它能帮助回答这些问题。
在这个模式中,Summary 视图(在 Timeline 的顶部 ) 显示了一些水平的栅栏,分别代表页面中的网络和 HTML 解析(蓝色 ),JavaScript(黄色 ),样式重计算和布局(紫色 ) 以及绘画和合成(绿色 ) 事件。重绘是浏览器事件,是为响应诸如窗口大小改变或者滚动之类的视觉变化而调用的。
CSS 属性的修改会对样式重新进行计算,而布局事件(即重排)是由元素位置的改变引起的。别担心记不住这些,在 Timeline 面板下方有图例告诉你。
在 Summary 视图下面是详情视图,包含了某个会话被记录后,相关类别的记录的详细内容。
每一个记录在左侧有用于说明的标题,右侧是时间轴区域。鼠标移到一个记录之上,会显示更多的提示信息,其中包括从开始录制到结束的时间 - 这非常有用,有必要多关注一下,特别是其中的调用栈信息。
点击调用栈(Call Stack)或者气泡提示中的超链接,会跳转到相应的 Javascript 代码行。如果你发现一个浏览器时间花费了过多的时间(可以从详细的气泡提示中的 Duration 知道),你也许会进一步去研究其原因。
回到记录列表,点击某个记录将其展开,可以看到更进一步的记录,描述了这个记录是由哪些事件组成的。
如果你只对某个特段的数据感兴趣,在 Summary 视图中通过点击和拖拽可以选择放大的区域。
改善帧率、动画及响应性能
Chrome 把你的应用展示到屏幕上需要生成每一幅帧,而帧模式可以让你可以深入到每一帧生成的内部细节。
作为平滑的体验,你看到的帧率最好一直保持在 30-60 fps,如果太低了,你的应用就会因为丢帧看上去混乱或者抖动。
在帧模式下,带阴影的竖条对应正在重计算样式、正在组合等等情况。每个竖条的透明区域对应于空闲时间,至少对于你的页面是空闲的。例如,假设第一帧用了 15ms
,下一帧用了 30ms
。通常每一帧都会按刷新率进行同步,这个例子中第二帧的渲染多花了 15ms
,导致第三帧错失了”真正“的硬件帧的时间,直接跳到下一帧的渲染。这样,第二帧的实际生效时间就加倍了。
正如 Andrey Kosyakov 的博文中提到的,即使你的程序没有很多动画,帧的概念也是有用的,因为浏览器在处理输入事件时会生成重复动作的序列。如果你在一帧中留有足够的时间处理这些事件,就会使你的程序看上去有更好的响应性,这意味着更好的用户体验。
让我们再次放大 Summary 视图,看一下那些不满足帧率的帧,你就会发现浏览器(以及程序的行为)对此的影响了。
举个例子,最近我们使用帧视图(以及事件视图)发现我们的程序有过多的图像解码,这是因为浏览器需要不断的实时的调整图片的大小。
作为替代方案,为图片预先准备好所有需要的尺寸,我们就避免了这些开销,从而达到 60 fps 的目标,对于最终用户来说更为平滑。
相关提示:通过在 Settings 菜单打开 Show FPS meter 选项,你可以在 DevTool 中打开实时的 FPS 计数器。
移动端
注意在移动端,如同 Paul 在 Breakpoint Ep 4演示的那样,动画和帧率都与桌面上大不一样,可以差几个数量级。要达到更高的帧率要更困难一些,而像 Timeline 的帧模式这样的工具(通过远程调试连接)可以帮你发现瓶颈所在。
检查耗时的绘制,是困难的
要想检查一段时间内的绘制(paint)是另一个挑战。如果你打算知道为什么某个特定的元素绘制的比较慢,你可以把 DOM 树中的部分元素设置成 display:none
将它们从布局/内容树中移除,并且设置 visibility:hidden
不让它们绘制。然后你可以用 Timeline 进行录制以便测量,看一下绘制时间,在强制重绘模式中可以观察(实验性的)绘制率。
减少内存使用与避免锯齿形曲线
你的应用有可能会遭受内存泄露问题,Memory 模式对于侦测这种问题的初期症状非常有用。
为使用这个功能,再花几分钟记录你与应用之间的交互,之后停止记录并检查。在 Summary 视图中,你会看到随着你在应用不同部分之间的跳转,这些动作引起的内存使用情况。其中既有内存使用的攀升,也有普通的垃圾回收发生。
浅蓝色的区域代表一个给定时间里,你的应用所使用的内存大小,而白色的区域是已经分配的内存总量。
如果你观察到 Summary 视图中有一个锯齿图形 ,这便是代表了应用的成本费用。例如,一个无参的 requestAnimationFrame
函数将带来垃圾,不管怎么说,你需要关注锯齿的陡峭度。如果它变得非常陡峭,说明你制造了太多的垃圾。
你可以进一步的在新的会话记录中,通过与应用之间的交互来测试这一点,空闲几分钟之后停止并再次查看结果。当应用处于空闲(或者你刚刚制造了许多垃圾),V8 引擎会执行一系列的垃圾收集过程。如果在你空闲之后,内存似乎从没有真正的降下来,那么说明你创造了太多的垃圾。
通过几个周期的垃圾回收,理想情况下内存视图的轮廓应该是扁平的。如果在两个 GC 周期间隔中它持续不断的上升(你看到会说它是一个阶梯函数),那么你可能会发生内存泄漏。
在 Memory 模式 Detail 视图的左侧,你能发现三个选项:文档计数(Document count),DOM 节点计数 (DOM node count) 以及事件侦听计数 (Event listener count)。
DOM 节点计数图显示了保存在内存中的已创建 DOM 节点的数量(即尚有待垃圾回收的那些节点),而另外两个选项类似的显示了事件侦听与documents/iframe的实例数。要是你只想看特定的计数类型,可以在 Details 视图取消其它的选择以便将它们隐藏。
我们现在知道了有潜在内存泄漏的可能,但是我们应该定位它们的源头。我们可以使用另外的堆分析仪(Heap Profiler)功能,它就在 Profiles 面板中。
评论 (0)