浏览器视角看——重绘重排
Posted 胖鹅68
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浏览器视角看——重绘重排相关的知识,希望对你有一定的参考价值。
文章目录
一、参考
强烈推荐
—— 李兵老师的 《浏览器工作原理与实践》
二、 渲染引擎概述
2.1 渲染流程各个阶段
-
渲染进程将 html 内容转换为能够读懂的 DOM 树结构。
-
渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
-
创建布局树,并计算元素的布局信息。
-
对布局树进行分层,并生成分层树。
-
为每个图层生成绘制列表,并将其提交到合成线程。
-
合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
-
合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
-
浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
三、渲染引擎阶段概述
3.1 构建 DOM 树
为什么要构建 DOM 树呢?
这是因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。
DOM 和 HTML 内容几乎是一样的,但是和 HTML 不同的是,DOM 是保存在内存中树状结构,可以通过 javascript 来查询或修改其内容
。
3.2 样式计算
1. 把 CSS 转换为浏览器能够理解的结构
CSS 样式来源主要有三种:
- 通过 link 引用的外部 CSS 文件
- <style>标记内的 CSS
- 元素的 style 属性内嵌的 CSS
CSS为什么要转换?
和 HTML 文件一样,浏览器也是无法直接理解这些纯文本的 CSS 样式
,- 所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。
document.styleSheets
CSS 文本全部转换为 styleSheets 结构中的数据,并且该结构同时具备了查询和修改功能,这会为后面的样式操作提供基础
。
可以参考阮一峰老师的 —— CSS操作
2. 转换样式表中的属性值,使其标准化
如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。
3. 计算出 DOM 树中每个节点的具体样式
如何计算呢?——CSS 的继承规则和层叠规则
- 需要特别提下 UserAgent 样式,它是浏览器提供的一组默认样式,如果你不提供任何样式,默认使用的就是 UserAgent 样式。
- CSS 继承就是每个 DOM 节点都包含有父节点的样式
- 层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法
3.3 布局阶段
计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局
。
1. 创建布局树
你可能注意到了 DOM 树还含有很多不可见的元素,比如 head 标签,还有使用了 display:none 属性的元素。所以在显示之前,我们还要额外地构建一棵只包含可见元素布局树
。
2. 布局计算
计算布局树节点的坐标位置了
3.4 分层
为什么要分层?
因为页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。
如果你熟悉 PS,相信你会很容易理解图层的概念,正是这些图层叠加在一起构成了最终的页面图像。
那么需要满足什么条件,渲染引擎才会为特定的节点创建新的图层呢?(满足之一)
第一点,拥有层叠上下文属性的元素会被提升为单独的一层。
层叠上下文?
https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
第二点,需要剪裁(clip)的地方也会被创建为图层。
3.5 绘制
渲染引擎实现图层的绘制与之类似,会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表
绘制列表中的指令其实非常简单,就是让其执行一个简单的绘制操作,比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制
。所以在图层绘制阶段,输出的内容就是这些待绘制列表。
3.6 分块
合成线程会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512
3.7 光栅化
合成线程会按照视口附近的图块来优先生成位图
,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图
图块是栅格化执行的最小单位
渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的
3.8 合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。
浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad命令,然后根据DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
四、重排、重绘、合成
4.1 重排
浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。
无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。
4.2 重绘
没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。
相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
4.3 合成
渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成
效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率
五、如果下载 CSS 文件阻塞了,会阻塞 DOM 树的合成吗?会阻塞页面的显示吗?
- 那么DOM解析器会先执行JavaScript脚本,执行完成之后,再继续往下解析。
<html>
<body>
极客时间
<script>
document.write("--foo")
</script>
</body>
</html>
- 当解析到JavaScript的时候,会先暂停DOM解析,并下载foo.js文件,下载完成之后执行该段JS文件,然后再继续往下解析DOM。这就是JavaScript文件为什么会阻塞DOM渲染。
<html>
<body>
极客时间
<script type="text/javascript" src="foo.js"></script>
</body>
</html>
- 当我在JavaScript中访问了某个元素的样式,那么这时候就需要等待这个样式被下载完成才能继续往下执行,所以在这种情况下,CSS也会阻塞DOM的解析。
<html>
<head>
<style type="text/css" src = "theme.css" />
</head>
<body>
<p>极客时间</p>
<script>
let e = document.getElementsByTagName('p')[0]
e.style.color = 'blue'
</script>
</body>
</html>
结论: 所以JS和CSS都有可能会阻塞DOM解析
以上是关于浏览器视角看——重绘重排的主要内容,如果未能解决你的问题,请参考以下文章