页面的绘制时间(paint time)是每一个前端开发都需要关注的的重要指标,它决定了你的页面流畅程度。而如何去观察页面的绘制时间,找到性能瓶颈,可以借助Chrome的开发者工具。
本文主要介绍Chrome渲染分析工具 Rendering。
如上图,按F12调出开发者工具,然后按“ESC”调出Rendering界面。
以上5个选项的意思如下:
1、Show paint rectangles 显示绘制矩形
2、Show composited layer borders 显示层的组合边界(注:蓝色的栅格表示的是分块)
3、Show FPS meter 显示FPS帧频
4、Enable continuous page repainting 开启持续绘制模式 并 检测页面绘制时间
5、Show potential scroll bottlenecks 显示潜在的滚动瓶颈。
1、Show paint rectangles
开启 显示绘制矩形 这个选项,可以看到绿色的框(之前的版本都是红色的框,现在改绿色了,呵呵),这个绿色的框,表示页面正在绘制的区域,即是页面正在渲染,发生绘制操作的区域。 这是用来了解滚动时页面表现的第一个指示器。
鼠标移到图片上,可以发现css3动画的位移,而css3动画的位移导致页面重绘,重绘的区域即是绿色框住的部分。细心的朋友可能会发现,这个绿色框住的部分,并不仅仅就是刚好那个div所在的区域,而涉及到周边的位置。发生这种情况的原因,是页面的重绘是个连带反应,会影响周边元素。
开启这个选项之后,可以做一些常规的页面交互操作,如Slider切换,拍拍网左侧导航mouse over时效果,可以看到页面效果所影响的范围。
再比如滚动页面,拍拍首页会出现一个返回顶部的按钮,滚动的时候,会发现返回顶部这个区域在不停的进行重绘,而返回顶部是position:fixed
定位的。这也解释了为什么fixed定位是最耗性能的属性之一
如果这个时候,我们给头部的再加个position:fixed
,然后滚动页面时,会发现整个页面都几乎是绿色框住了,这主要是所有具有fixed定位的元素,在页面绘制时会处于同一个渲染层级上,一头一尾的fixed无疑会导致整个页面进行重绘,性能非常差。
这里提到一个渲染层级的概念,webkit内核的浏览器在进行页面绘制、渲染并最终展示在浏览器窗口上,实际上就像是Photoshop上的图层,不同的图层进行叠加最后生成一个图片的过程。
这个层的详细介绍,我下一篇文章会详细介绍,这里先卖个关子。
回到之前的话题,既然绿色框住的部分表示页面重绘,那哪些操作会导致重绘呢?
影响页面重绘的因素
主要有2大类:
1、页面滚动
2、互动操作
1).Dom节点被Javascript改变,导致Chrome重新计算页面的layout。
2).动画不定期更新。
3).用户交互,如hover导致页面某些元素页面样式的变化。
4).调整窗口大小 和 改变字体
5).内容变化,比如用户在input框中输入文字
6).激活 CSS 伪类,比如 :hover
7).计算 offsetWidth 和 offsetHeight 属性
8).增加或者移除样式表
影响重绘的因素很多,这里列举了部分在前端开发的过程中常见的操作:
1、应该尽量避免重绘,并且尽可能的使绘制区域最小,以提升页面性能。
就上面拍拍网的例子,一头一尾加上fixed定位导致整个页面重绘,是不可取的。也许你分析完后以后都不敢用fixed,但是可能在实际工作中这种情况无法避免(设计师或产品经理要求)。何东西都有它适用的地方,重要的是作为前端人员,你应该能够测量并知道你写的代码所带来的性能损耗及所造成的影响。
2、同时避免组合触发,如滚动的时候同时执行hover效果操作,一个例子(Expensive Scrolls),
在滚动的时候同时也有可能触发页面模块的hover效果,而hover效果用了box-shadow
、border-radius
等耗性能大的样式。从而可能导致丢帧现象。
如何去优化:技巧在于滚动时,关闭模块的hover效果,然后设定一个计时器,时间到了再把hover打开。意思是我们保证在滚动时不去执行昂贵的互动事件重绘。当你停止动作一段时间后,我们再将动画开启。
2.show composited layer borders
中文可翻译为:显示层的组合边界。
我们知道,在页面最终是由多个“图层”渲染而成。勾上这个选项,页面上的“layer(层)”会加上一个黄色的边框显示出来,如下图的天猫首页头部所示:
其中:
- 黄色边框:用于显示页面上的layer
- 蓝色栅格线:表示的是分块,这些分块可以看作是比层更低一级的单位
当然,还有其他颜色的边框线,比如图片如果单独有个layer的话,边框线是蓝色的。
使用这个工具,可以查看当前页面的layer情况,更好的发现页面不需要的layer将之清除。
layer存在的意义
在弄明白这个问题之前,我们需要先了解一个dom元素最终是如何转变为我们屏幕上可视的图像。在概念上讲,可简单的分为四个步骤:
- 获取 DOM 并将其分割为多个层
- 将每个层独立的绘制进位图中
- 将层作为纹理上传至 GPU
- 复合多个层来生成最终的屏幕图像。
可以将这个过程理解为设计师的Photoshop文件。在ps源文件里,一个图像是由若干个图层相互叠加而展示出来的。分成多个图层的好处就是每个图层相对独立,修改方便,对单个图层的修改不会影响到页面上的其他图层。
基于photoshop的图层理念来理解web端的层,那么就很容易理解了。layer存在的意义在于:用最小的代价来改变某个页面元素。
我们可以将某个css动画或某个js交互效果把它抽离到一个单独的渲染层,这样可以加快渲染的效率。
如何创建layer
- 3D 或透视变换(perspective transform) CSS 属性
- 使用加速视频解码的
<video>
元素 - 拥有 3D (WebGL) 上下文或加速的 2D 上下文的
<canvas>
元素 - 混合插件(如 Flash)
- 对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
- 拥有加速 CSS 过滤器的元素
- 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
- 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
- 在webkit内核的浏览器中,如果有上述情况,则会创建一个独立的layer。
在webkit内核的浏览器中,如果有上述情况,则会创建一个独立的layer。
其中第一点是最常用的手段,比如我们有时候给一个css效果加上transform: translateZ(0);
,目的就是为了创建一个独立的layer。
另外还有另外一个css属性:will-change
也能实现同样的效果。
layer 过多带来的问题
还是拿photoshop来做比喻,一个ps文件如果有非常多的图层,那么这个文件肯定是非常大的。那对于web端也是一样,创建一个新的渲染层,它得消耗额外的内存和管理资源。当在内存资源有限的设备,比如手机上,由于过多的渲染层来带的开销而对页面渲染性能产生的影响,甚至远远超过了它在性能改善上带来的好处。
举个栗子,我们在天猫首页上加入css:* {-webkit-transform: translateZ(0);}
。 然后使用timeline
可以看到,天猫的渲染耗时非常严重。
其影响的是页面渲染的最后一个环节:Composite Layers
。
那么,一个合理的策略是:当且仅当需要的时候才为元素创建渲染层。
更直观的查看页面layer
除了rendering 里提供的show composited layer borders
选项外,还有一个更为直观的3d图像展示:
先选中timeline
的某一帧,然后选择下面的layer
标签tab,在右侧的区域就可以看到整个页面的3d图层了。
在这个视图中,你可以对这一帧中的所有渲染层进行扫描、缩放等操作,同时还能看到每个渲染层被创建的原因。
3.show FPS meter
show fps meter
可以理解为显示FPS帧频/帧数。开启这个选项后,右上角会实时显示当前页面的FPS。
先简单科普一下啥是FPS。FPS全称叫 Frames Per Second (每秒帧数)。帧数越高,动画显示的越流畅。一般的液晶显示器的刷新频率也就是 60HZ。也就是说,要想页面上的交互效果及动画流畅。那么FPS稳定在60左右,是最佳的体验。。据悉 ios上的交互效果都是60FPS呢。
记得以前做Flash游戏的时候,FPS帧数是游戏流畅度的一个重要指标。在web端,道理也是一样。
科普完毕,回到正题。chrome提供的show FPS meter
选项,在我们制作测试页面交互及动画性能时非常有用。同时它也提供了当前页面的GPU占有率给我们。
参考:
http://www.html5rocks.com/en/tutorials/speed/unnecessary-paints/
https://developers.google.com/web/fundamentals/performance/rendering/stick-to-compositor-only-properties-and-manage-layer-count
http://www.html5rocks.com/en/tutorials/speed/layers/
https://developer.chrome.com/devtools/docs/rendering-settings