主要是整理当浏览器拿到html和css之后怎么把它们渲染成我们能看到的页面.
GUI渲染线程大致流程如下:
接下来我们分环节从前往后得分析渲染流程
DOM tree 和CSSOM tree
什么是tree?就是树,它代表了一直关系.节点和节点之间是有关系的,根节点是谁,兄弟节点有哪些等.所以DOM tree实际上就是dom元素本体的关系树,而CSSOM tree则是dom元素的样式树.用于描述它有啥样式.
浏览器创建树的过程
- 转换(Conversion):浏览器从磁盘或网络读取 HTML 的字节码,并根据文件的指定编码(例如 UTF-8)将其转换成对应的字符。
- 令牌化(Tokenizing):浏览器将字符串转换成W3C HTML5 标准规定的各种 token,例如
<html>
、<body>
,以及其他尖括号内的字符串。每个 token 都具有一定特殊含义和规则。 - 词法分析(Lexing):将 token 转换成定义其属性和规则的“对象”。
- DOM tree和 CSSOM tree:HTML 标签定义了不同标签之间的关系(某些标签包含在其他标签中),所创建的对象链接在树状数据结构中,该数据结构还捕获原始标签中定义的父——子关系:HTML 是 body 的父对象,body 是段落的父对象,依此类推
注意点:
1. CSSOM tree生成过程类似.但结构不一定一样.CSS tree是根据.css文件返回的结果生成的tree.也就是说有些dom元素并没有被赋予额外样式的时候便不会出现在CSSOM tree中.
2.另外CSSOM tree的构建是阻塞性质的,当CSSOM tree构建完成之前并不会进行Rendering Tree的构建,可通过 CSS“媒体类型”和“媒体查询” 来控制不必要的CSS文件引入.而且要尽早的引入css文件.
3.DOM tree和CSSOM tree通常是同步进行的,甚至可以一边生成DOM tree一边把它和已解析好的CSSOM tree进行结合.但是只有CSSOM tree已经解析完成了才进行Rendering Tree的构建
4.写CSS时嵌套越多,在生成CSSOM tree的时候越费劲.所以尽量少而精.
Rendering Tree
当DOM tree 和CSSOM tree构建完成之后,需要将他们合成一个完整的tree即需要在页面上展示的元素的tree,既包含dom类型等又包含样式的tree ,这个tree即Rendering Tree.但是,它不仅仅是DOM tree +CSSOM tree这么简单,因为有些元素并不需要展示.
它包含以下原则:
1.DOM树的document节点
2.DOM树中的“可视节点”;Webkit不会为非可视节点创建RenderObject节点.(包含"head"、“script”就是典型的“非可视节点”),另外还有些display:none的节点.而visibility: hidden只是隐藏并不是移除
3.特殊情况下的匿名RenderObject节点,这些节点不应用于DOM树中的节点.比如给行内元素加上"display:inline"时的匿名RenderBlock对象.
也就是说从数量上来看DOM树和Rendering Tree不一定相等,也没有谁覆盖谁一说,Rendering Tree会在DOM树基础上做一些减法同时也会做一些加法.
RenderLayer tree
RenderLayer tree可以算是对Rendering Tree进行在分类,而分类的依据就是"图层".大概就是说明了不同的图层有不同的RenderObject.RenderLayer和RenderObject为一对多的,因为一个图层可能会有多个RenderObject.
分类依据:
1.position:显式指定 CSS position 属性的 RenderObject 节点
2.opacity:有透明度的 RenderObject 节点
3.有 overflow,alpha 和 reflection 的样式 RenderObject 节点
4.filter:有 filter 样式的 RenderObject 节点
5.Canvas:使用 Canvas 2D 和 3D(WebGL)技术的 RenderObject 节点
6.video :video元素对应的 RenderObject 节点
总结一下基本都是单一图层处理不了的属性或者功能才会有额外的图层,假如不在以上情况里的RenderObject怎么办?它们会继承父元素所在的"图层",也就是说所有的RenderObject如果在以上情况,就单独再开一个图层,把它和它的子元素们都放在一个图层.最终形成了一个RenderLayer tree.RenderLayer tree和Rendering Tree的元素数量上应该是保持一致的.
Layout(布局)
当浏览器创建 RenderObject 对象之后,每个对象并不知道自己在设备视口内的位置、大小等信息。浏览器根据盒模型来计算它们的位置、大小等信息的过程称为布局计算(重排)。布局计算是一个递归的过程,这是因为一个节点的大小通常需要先计算它的孩子节点的位置、大小等信息。为了计算节点在页面中的确切大小和位置,浏览器会从 RenderObject tree 的根节点开始进行遍历。
盒模型:包含内容,边距边框等属性,规定了RenderObject以什么规则在浏览器中渲染.
GrphicsLayer tree
根据页面渲染方式的不同,硬件加速的页面渲染时还会有个GrphicsLayer tree.因为硬件加速的页面渲染方式有GPU来绘制,所以需要为其提供内存,而它需要对RenderLayer tree进行再分类来分配.大部分情况是与RenderLayer tree相同的,也就是说基本每层RenderLayer tree都能分到内存,另外多了几个属性
transform
:transform属性的会获得单独的内存.
z-index:有一个 z-index 比自己小的合成层
.
Rendering
解析完成之后的渲染过程基本就两个.先分图层绘制,再合成到一个图层形成我们能看到的一个完整的页面.不过软件渲染方式并不会经过合成的过程.
然后我们再看看不同的修改对于浏览器的影响.首先可以确认的是,通常情况下,只要首次渲染完成了,js对dom的样式修改便是最早从layout开始的,之前的操作浏览器并不会再执行.
重绘
如果是修改color之类的属性,并不影响布局,那么对于浏览器来说,它只需要在rendering的时候重新绘制就行
回流
如果是修改height之类的属性,影响了页面布局,那么需要根据新修改的属性,重新通过盒模型进行Layout(布局),然后再进行绘制合成.
composite
如果修改和合成层的分类依据属性,比如transform
和opacity等属性,那我只需要在合成的时候稍微调整一下就好,图层里面的内容并不会改变,所以甚至不需要重新绘制.
优化
1.我们可以把对某个元素的多属性修改改成修改其类名进行统一修改,避免多次修改.
2.将修改提升到合成层,比如利用transform
也可以实现类似修改宽高的功能,而且减少了layout和rendering的过程.
所以对于浏览器来说,压力最小的修改方式是修改合成依据属性,然后再是重绘再是回流.
主要借鉴文章:浏览器渲染简述