渲染性能

原文地址 与 译文地址

要想编写高性能的 web 站点或应用,你需要充分了解浏览器是如何处理 HTML/JavaScript/CSS 的,从而确保你写的代码(或引用的第三方代码)是尽可能高效的。

60帧与设备刷新率

当今大多数设备的屏幕刷新率都是 60次/秒 。因此,如果在页面中有一个动画或渐变效果,或者用户正在滑动页面,那么浏览器渲染动画或页面的每一帧的速率,也需要跟设备屏幕的刷新率保持一致。

也就是说,浏览器对每一帧画面的渲染工作需要在16毫秒(1秒 / 60 = 16.66毫秒)之内完成。但实际上,在渲染某一帧画面的同时,浏览器还有一些额外的工作要做(比如渲染队列的管理,渲染线程与其他线程之间的切换等等)。因此单纯的渲染工作,一般需要控制在10 毫秒之内完成,才能达到流畅的视觉效果。如果超过了这个时间限度,页面的渲染就会出现卡顿效果,也就是常说的掉帧,它是很糟糕的用户体验。

像素渲染流水线

在编写 web 页面时,你需要理解你所写的页面代码是如何被转换成屏幕上显示的像素的。这个转换过程可以归纳为这样的一个流水线,包含五个关键步骤:
frame-full

JavaScript
一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的animate函数做一个动画、对一个数据集进行排序、或者往页面里添加一些 DOM 元素等。当然,除了JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如:CSS Animations, Transitions 和 Web Animation API。
Style calculations(计算样式)
这个过程是根据 CSS 选择器,比如.headline.nav > .nav_item,对每个 DOM 元素匹配对应的 CSS 样式。这一步结束之后,就确定了每个 DOM 元素上该应用什么 CSS 样式规则。
Layout(布局)
上一步确定了每个DOM 元素的样式规则,这一步就是具体计算每个 DOM 元素最终在屏幕上显示的大小和位置。Web 页面中元素的布局是相对的,因此一个元素的布局发生变化,会联动地引发其他元素的布局发生变化。比如,<body>元素的宽度的变化会影响其子元素的宽度,其子元素宽度的变化也会继续对其孙子元素产生影响。因此对于浏览器来说,布局过程是经常发生的。
Paint(绘制)
本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个 DOM 元素所有的可视效果。一般来说,这个绘制过程是在多个图层上完成的。
Compositing(组合)
由上一步可知,对页面中 DOM 元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。

上述过程的每一步中都有产生掉帧的问题,因此一定要弄清楚你的代码将会运行在哪一步。

有时你可能会听到“栅格化(rasterize)”与绘制一起使用。这是因为“绘制”这个动作实际是包含两步:1.产生系列的格子。 2.往格子中填充像素。这个过程后来被称之为“栅格化(rasterization)”,所以你在 DevTools 中看到的绘制记录,你应该要知道其中已经包含了栅格化这一过程。

你不需要了解所有帧画面渲染流程上的所有流程。实际上,当你修改视图的时候,有三种方式会重新生成一个帧画面,无论你是修改 JavaScript, CSS 还是 Web Animations。

1.JS/CSS > Style > layout > Paint > Composite

frame-full

如果你修改布局属性(元素的几何形状),比如宽度,高度以及位置。,那么浏览器会检查哪些元素需要重新布局,然后对页面激发一个 reflow 过程完成重新布局。被 reflow 的元素,接下来也会激发绘制过程,最后激发渲染层合并过程,生成最后的画面。

2.JS/CSS > Style > Paint > Composite

frame-no-layout

如果你只是修改了绘制属性,比如说背景图片,字体颜色,阴影等,这些属性不属于页面布局,因此浏览器会在完成样式计算之后,跳过布局过程,只做绘制和渲染层合并过程。

3.JS/CSS > Style > Composite

frame-no-layout-paint

如果你修改的属性既不属于布局,也不属于绘制,那么浏览器会在完成样式计算之后,跳过布局和绘制的过程,直接做渲染层合并。

第三种方式在性能上是最理想的,对于动画和滚动这种负荷很重的渲染,我们要争取使用第三种渲染流程。

如果你想知道哪些 CSS 属性会触发这三种方式,可以访问 csstriggers.com ,如果想对高性能动画有更多了解,参考:使用渲染层合并属性

性能优化是一门做减法的艺术。我们首要要尽力简化页面渲染过程,然后要使渲染过程的每一步都尽量高效。在很多时候,我们需要跟浏览器一起努力来创建高性能web应用,而不是跟浏览器对着干。要记住,以上列举的流水线中的每一步,在时间消耗上是各不相同的,有些步骤是相对更费时的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注