浏览器渲染机制理解

浏览器渲染机制的复习和整理,主要来自酷壳网和黑色影子在segmengful的文章,对于层和复合层的概念还需要进一步加深学习理解。链接附在文末。

解析生成DOM Tree/CSS Rule tree

浏览器渲染机制理解_第1张图片
image.png

1)浏览器会解析三个东西:

  • 一个是HTML/SVG/XHTML,产生一个DOM Tree。
  • CSS,解析CSS会产生CSS规则树。
  • Javascript,脚本,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.

2)解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意:

Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。

CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。

浏览器渲染机制理解_第2张图片
image.png

然后,计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。
3)最后通过调用操作系统Native GUI的API绘制。

渲染的流程基本上如下(黄色的四个步骤):

  1. 计算CSS样式
  2. 构建Render Tree
  3. Layout – 定位坐标和大小,是否换行,各种position, overflow, z-index属性 ……
  4. 正式开画
浏览器渲染机制理解_第3张图片
image

注意:上图流程中有很多连接线,这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout,有些改变不会,就是那些指到天上的箭头,比如,修改后的CSS rule没有被匹配到,等。

这里重要要说两个概念,一个是Reflow,另一个是Repaint。这两个不是一回事。

  • Repaint——屏幕的一部分要重画,比如某个CSS的背景色变了。但是元素的几何尺寸没有变。

  • Reflow——意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflow)reflow 会从这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。

Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程是非常痛苦和耗电的。

减少reflow/repaint
下面是一些Best Practices:

下面是一些针对reflow和repaint的最佳实践:

  • 不要一条一条地修改dom的样式,尽量使用className一次修改。

  • 将dom离线后修改

    • 使用documentFragment对象在内存里操作dom。

    • 先把dom节点display:none;(会触发一次reflow)。然后做大量的修改后,再把它显示出来。

    • clone一个dom节点在内存里,修改之后;与在线的节点相替换。

  • 不要使用table布局,一个小改动会造成整个table的重新布局。

  • transform和opacity只会引起合成,不会引起布局和重绘。

从上述的最佳实践中你可能发现,动画优化一般都是尽可能地减少reflow、repaint的发生。关于哪些属性会引起reflow、repaint及composite,你可以在这个网站找到https://csstriggers.com/。

Composite

在reflow和repaint之后,浏览器会将多个复合层传入GPU;进行合成工作,那么合成是如何工作的呢?

GPU实际上可以看作一个独立的计算机,它有自己的处理器和存储器及数据处理模型。当浏览器向GPU发送消息的时候,就像向一个外部设备发送消息。

浏览器向GPU发送数据也需要先创建一个载体;只不过GPU距离CPU很近,不会像远程服务器那样可能几千里那么远。但是对于远程服务器,2秒的延迟是可以接受的;但是对于GPU,几毫秒的延迟都会造成动画的卡顿。

浏览器向GPU发送的数据载体是什么样?这里给出一个简单的制作载体,并把它们发送到GPU的过程。

画每个复合层的图像

准备图层的数据

准备动画的着色器(如果需要)

向GPU发送数据

为了仅发生composite,我们做动画的css property必须满足以下三个条件:

不影响文档流。

不依赖文档流。

不会造成重绘。

满足以上以上条件的css property只有transform和opacity。你可能以为position也满足以上条件,但事实不是这样,举个例子left属性可以使用百分比的值,依赖于它的offset parent。还有em、vh等其他单位也依赖于他们的环境

浏览器在动画执行之前就知道动画如何开始和结束,因为浏览器没有看到需要reflow和repaint的操作;浏览器就会画两张图像作为复合层,并将它们传入GPU。

这样做有两个优势:

  • 动画将会非常流畅

  • 动画不在绑定到CPU,即使js执行大量的工作;动画依然流畅

优化技巧总结

减少浏览器的重排和重绘的发生。

不要使用table布局。

css动画中尽量只使用transform和opacity,这不会发生重排和重绘。

尽可能地只使用css做动画。

css动画有一个重要的特性,它是完全工作在GPU上。因为你声明了一个动画如何开始和如何结束,浏览器会在动画开始前准备好所有需要的指令;并把它们发送给GPU。而如果使用js动画,浏览器必须计算每一帧的状态;为了保证平滑的动画,我们必须在浏览器主线程计算新状态;把它们发送给GPU至少60次每秒。除了计算和发送数据比css动画要慢,主线程的负载也会影响动画; 当主线程的计算任务过多时,会造成动画的延迟、卡顿。

所以尽可能地使用基于css的动画,不仅仅更快;也不会被大量的js计算所阻塞。

避免浏览器的隐式合成。

改变复合层的尺寸

参考:
https://segmentfault.com/a/1190000008015671
https://coolshell.cn/articles/9666.html/comment-page-2#comments

你可能感兴趣的:(浏览器渲染机制理解)