05 渲染流程:HTML、CSS和JavaScript是如何变成页面的

Posted by CodingWithAlice on April 9, 2021

05 渲染流程:HTML、CSS和JavaScript是如何变成页面的

渲染流程总结:构建DOM树、样式计算、布局、分层、绘制、栅格化、合成显示,如下图所示

image-20210410135604001

流程总结:

  • 渲染进程 的主线程将 HTML 内容转换为能够读懂的 DOM 树结构
  • 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出DOM节点的样式
  • 创建 布局树,并计算元素的布局信息
  • 对布局树进行分层,并生成 分层树
  • 为每个图层生成 绘制列表,并将其提交到合成线程
  • 合成线程 将图层分成图块,并在 光栅化线程池 中将图块转换成位图
  • 合成线程发送绘制图块命令 DrawQuad 给浏览器进程
  • 浏览器进程 根据 DrawQuad 消息生成页面,并显示到显示器上

1、构建DOM树

背景:浏览器无法直接理解和使用HTML,所以需要将HTML转换为浏览器能够理解的结构:DOM树。

【输入】:HTML文件 【输出】:树状结构的DOM

HTML文件 –> HTML解析器解析 –> 树状结构的DOM

image-20210410135604001

2、样式计算

背景:浏览器也是无法直接理解这些纯文本的CSS样式

【输入】:CSS样式表 【输出】:styleSheets + 样式

1、渲染引擎把CSS文本转换为浏览器能够理解的结构 – styleSheets

2、转换样式表中的属性值,使其标准化 例如:2rem –> 32px

image-20210410135810291

3、计算出DOM树中每个节点的具体样式

  • 继承规则
  • 层叠规则

3、布局阶段

【输入】:DOM树(含样式) 【输出】:布局树

  • 创建 布局树(所有 不可见的节点 都没有包含到布局树中 - visibility:hidden 节点还是在的)

  • 布局计算(计算出DOM树中 可见元素 的几何位置)

注意⚠️:

1、display:none 的节点不会被加入 Render Tree,而 visibility: hidden 则会,所以,如果某个节点最开始是不显示的,设为 display:none 是更优的。

2、display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发生位置变化。

3、有些情况下,比如修改了元素的样式,浏览器 并不会立刻 reflowrepaint 一次,而是会把这样的操作积攒一批,然后做一次 reflow,这又叫异步 reflow 或增量异步 reflow

4、但是在有些情况下,比如 resize 窗口,改变了页面默认的字体等。对于这些操作,浏览器会马上进行 reflow

4、分层

背景:渲染引擎需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)

【输入】:布局树 【输出】:分层树

满足以下条件的节点会被提升为单独的一个图层,其他的节点就从属于父节点的图层:

  • 1、拥有层叠上下文属性的元素

    image-20210410135850074

  • 2、需要剪裁(clip)的地方

    内容超出了元素的大小,例如div固定宽高后放入许多文字,渲染引擎会为文字部分单独创建一个层,如果出现滚动条,滚动条也会被提升为单独的层

    image-20210410135912798

5、图层绘制

【输入】:图层(分层)树 【输出】:为每个图层生成绘制列表,提交到合成线程

1、把每一个图层的绘制拆分成很多小的 绘制指令(一个简单的绘制操作) 2、然后再把这些指令按照顺序组成一个 待绘制列表(一个元素通常需要好几条绘制指令)

6、栅格化(raster)操作

栅格化:将 图块转换为位图

【输入】:绘制列表 【输出】:将图层分成图块,在光栅化线程池中将图块转换成位图

1、绘制操作是由渲染引擎中的 合成线程 来完成,合成线程会将图层划分为图块(tile),按照 视口附近 的图块来优先生成位图 2、渲染进程维护了一个 栅格化的线程池,所有的图块栅格化都是在线程池内执行的;通常,栅格化过程都会使用GPU来加速生成,生成的位图被保存在GPU内存中

image-20210410140005169

7、合成显示

一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给 浏览器进程,将其页面内容 绘制到内存 中,最后再将内存 显示在屏幕