研讨浏览器绘制和Web性能的注意事项

>>>点击获取更多文章<<<

浏览器绘制

Web浏览器将HTML、CSS和JavaScript转换成已完成的过程视觉化表示是相当复杂的,涉及到很好的”魔术”。下面是一组简化的浏览器步骤:

  1. 浏览器创建DOM和CSSOM(CSS Object Model)。
  2. 浏览器创建呈现树,其中考虑到来自CSSOM的DOM和样式(其中样式 display: none 是避开的)。
  3. 浏览器根据呈现树计算布局的几何形状及其元素。
  4. 浏览器逐个像素绘制,以创建我们在屏幕上看到的可视化表示。

在这里,我们重点对painting,也就是绘画,进行讨论。

所有这些步骤加在一起,对于浏览器来说,在加载时要做的工作很多.实际上,不仅仅是在加载上,而是在DOM(或CSSOM)被更改的任何时候。这就是为什么许多Web开发人员倾向于通过使用某种前端框架来部分解决这个问题,比如React,除了许多其他优点之外,它还可以帮助高度优化DOM中的更改,以避免不必要的重新计算或渲染。

你可能听说过这样的术语,状态(state), 组件渲染(component rendering),或不可变数据(Immutability)。所有这些都与DOM更改的优化有关,换句话说,只有在必要时才对DOM进行更改。

举个例子,Web应用程序的状态可能会发生变化,这将导致UI的更改。但是,某些或更多组件不受此更改的影响。React帮助我们的是,对实际受状态变化影响的元素进行限制写入,最终将呈现限制在Web应用程序的最小部分:

DOM/CSSOM → render tree → layout → painting

但是,浏览器绘制有其自身的特殊性,因为它甚至可以在不对DOM或CSSOM进行任何更改的情况下进行。

alt text

上图是页面性能摘要示例,图表是使用DevTools中的Chrome性能面板生成的(稍后将详细介绍),它显示了浏览器中的每个任务在重新加载页面后在记录的时间(0-7.12s)中花费了多少时间。正如你所看到的,绘画起着重要的作用,而这并不是一件坏事。在这个特殊的例子中,增加的绘画是由页面上的动画GIF和canvas drawing(在60 fps)的组合造成的,两者都不会导致DOM或其样式的任何更改,同时仍然触发绘画。

另一个可以在没有任何外部干预的情况下导致绘画的特性的好例子是css的animation属性,并且与动画GIF或canvas相比,它在Web上更常见。动画通常由用户触发,如悬停houver,感谢animation和@keyframes,我们甚至可以创建相当复杂的动画在页面上不断运行,无需付出多大的努力。

有些人可能没有意识到,这些动画很容易失控,并不断触发绘画,这可能会耗费我们大量的浏览器处理能力。当然,有一些规则可以用来避免绘画。最明显的就是将元素的操作限制在css的transform和opacity属性,在默认情况下不会触发画图,除非存在一些特殊情况,例如动画SVG路径。

Paint flashing

你可能知道Chrome有DevTools。你可能不知道的是一个小小的捷径(Mac上的Shift+Cmd+P,PC上的Control+Shift+P)。内DevTools打开一个搜索栏命令菜单。

alt text

render 面板 估计会引起你的注意,输入render,找到Show Rendering,回车确认。可以看到一些有趣的选项,当涉及到在web上调试动画时,这些选项可能非常有用,例如如图的FPS meter。

alt text

Layer borders和paint flashing也是有趣的工具。Layer borders用于显示由浏览器呈现的层的边框,以便于识别任何转换或大小更改。paint flashing用于突出显示浏览器被迫重新绘制的网页区域。大家可以将他们打钩然后找个网页重信刷新下看下效果。

个案研究

当设计提出了要求弄出嘈杂的背景要求,也就是老电视没信号的那种花屏幕效果(一点一点雪花点)。众所周知,GIF有许多问题,其中性能当然是其中之一,所以我肯定不能在整个页面背景中使用GIF。

在这种情况下,使用JavaScript绝对是一种选择,使用画布canvas也会有所帮助。然而,所有这一切似乎有点过分,因为只是有一个背景。我决定选择只使用CSS的方法。

我的解决方案是把一个小的“噪音”png图像作为一个background-image,启用background-repeat把它扔到单色背景上。如何达到噪音效果呢?用“无限的CSS动画”效果呀!通过设置background-position在200毫秒的时间内转换为不同的值。网页效果请点击 http://demo.zhangbing.name/demoa1.html,我们通过transform或opacity替换background-position 进行使用,代码在我的个人demo网页上http://demo.zhangbing.name/demoa2.html, 上面也提到过css的transform和opacity属性可以避免绘画,打开刚才说的 render面板,找到 Paint flashing 选项,在两个不同制作的网页上,进行点击切换,会发觉demoa1效果,也就是用了 background-position的那种方案,会进行重新绘画,而且消耗计算机性能。对比如下图

alt text