05 渲染流程:HTML、CSS和JavaScript是如何变成页面的
渲染流程总结:构建DOM树、样式计算、布局、分层、绘制、栅格化、合成显示,如下图所示
流程总结:
- 渲染进程 的主线程将
HTML
内容转换为能够读懂的 DOM 树结构 - 渲染引擎将
CSS
样式表转化为浏览器可以理解的styleSheets
,计算出DOM节点的样式 - 创建 布局树,并计算元素的布局信息
- 对布局树进行分层,并生成 分层树
- 为每个图层生成 绘制列表,并将其提交到合成线程
- 合成线程 将图层分成图块,并在 光栅化线程池 中将图块转换成位图
- 合成线程发送绘制图块命令
DrawQuad
给浏览器进程 - 浏览器进程 根据
DrawQuad
消息生成页面,并显示到显示器上
1、构建DOM树
背景:浏览器无法直接理解和使用HTML,所以需要将HTML转换为浏览器能够理解的结构:DOM树。
【输入】:HTML文件 【输出】:树状结构的DOM
HTML文件 –> HTML解析器解析 –> 树状结构的DOM
2、样式计算
背景:浏览器也是无法直接理解这些纯文本的CSS样式
【输入】:CSS样式表 【输出】:styleSheets + 样式
1、渲染引擎把CSS文本转换为浏览器能够理解的结构 – styleSheets
2、转换样式表中的属性值,使其标准化 例如:2rem –> 32px
3、计算出DOM树中每个节点的具体样式
- 继承规则
- 层叠规则
3、布局阶段
【输入】:DOM树(含样式) 【输出】:布局树
-
创建 布局树(所有 不可见的节点 都没有包含到布局树中 -
visibility:hidden
节点还是在的) -
布局计算(计算出DOM树中 可见元素 的几何位置)
注意⚠️:
1、display:none
的节点不会被加入 Render Tree
,而 visibility: hidden
则会,所以,如果某个节点最开始是不显示的,设为 display:none
是更优的。
2、display:none
会触发 reflow
,而 visibility:hidden
只会触发 repaint
,因为没有发生位置变化。
3、有些情况下,比如修改了元素的样式,浏览器 并不会立刻 reflow
或 repaint
一次,而是会把这样的操作积攒一批,然后做一次 reflow
,这又叫异步 reflow
或增量异步 reflow
。
4、但是在有些情况下,比如 resize
窗口,改变了页面默认的字体等。对于这些操作,浏览器会马上进行 reflow
。
4、分层
背景:渲染引擎需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
【输入】:布局树 【输出】:分层树
满足以下条件的节点会被提升为单独的一个图层,其他的节点就从属于父节点的图层:
-
1、拥有层叠上下文属性的元素
-
2、需要剪裁(clip)的地方
内容超出了元素的大小,例如div固定宽高后放入许多文字,渲染引擎会为文字部分单独创建一个层,如果出现滚动条,滚动条也会被提升为单独的层
5、图层绘制
【输入】:图层(分层)树 【输出】:为每个图层生成绘制列表,提交到合成线程
1、把每一个图层的绘制拆分成很多小的 绘制指令(一个简单的绘制操作) 2、然后再把这些指令按照顺序组成一个 待绘制列表(一个元素通常需要好几条绘制指令)
6、栅格化(raster)操作
栅格化:将 图块转换为位图
【输入】:绘制列表 【输出】:将图层分成图块,在光栅化线程池中将图块转换成位图
1、绘制操作是由渲染引擎中的 合成线程 来完成,合成线程会将图层划分为图块(tile),按照 视口附近 的图块来优先生成位图 2、渲染进程维护了一个 栅格化的线程池,所有的图块栅格化都是在线程池内执行的;通常,栅格化过程都会使用GPU来加速生成,生成的位图被保存在GPU内存中
7、合成显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给 浏览器进程,将其页面内容 绘制到内存 中,最后再将内存 显示在屏幕 上