likes
comments
collection
share

HTML解析阶段是如何生成DOM树和CSSOM树?

作者站长头像
站长
· 阅读数 39

一、构建DOM树

在说构建 DOM 树之前,我们首先需要知道,为什么要构建 DOM 树呢? 这是因为,浏览器是无法直接理解和使用 HTML 的,所以需要将 HTML 转化为浏览器能够理解的结构——DOM树。

在页面中,每个 HTML 标签都会被浏览器解析成文档对象,HTML 本质上就是一个嵌套结构,在解析时会把每个文档对象用一个树形结构组织起来,所有的文档对象都会挂在 document 上,这种组织方式就是 HTML 最基础的结构——文档对象模型(DOM),这棵树的每个文档对象就叫做DOM节点。

在渲染引擎中,DOM 有三个层面的作用:

  • 从页面的视角来看,DOM 是生成页面的基础数据结构
  • 从 JavaScript 脚本视角来看,DOM 提供给 JavaScript 脚本操作的接口,通过这套接口,JavaScript 可以对 DOM 结构进行访问,从而改变文档的结构、样式和内容
  • 从安全视角来看,DOM 是一道安全防护线,一些不安全的内容在 DOM 解析阶段会被拒之门外

在渲染引擎内部,HTML 解析器负责将 HTML 字节流转换为 DOM 结构,其转化过程如下: HTML解析阶段是如何生成DOM树和CSSOM树?

1. 字符流 → 词(token)

HTML结构会首先通过分词器将字符流拆分为词(token),Token分为 Tag Token 和文本 Token,下面来看一个HTML代码是如何被拆分的:

<body>
    <div>
        <p>hello world</p>
    </div>
</body>

对于这句代码,可以拆成词: HTML解析阶段是如何生成DOM树和CSSOM树? 可以看到,Tag Token 又分 StartTag 和 EndTag,<body><div><p>就是 StartTag ,</body></div></p>就是 EndTag,分别对应图中的蓝色和红色块,文本 Token 对应绿色块。 ​

这里会通过状态机将字符拆分成 token,所谓的状态机就是将每个词的特征逐个拆分成独立的状态,然后再将所有词的特征字符合并起来,形成一个连通的图结构。那为什么要使用状态机呢?因为每读取一个字符,都要做一次决策,这些决策都和当前的状态有关。 ​

实际上,状态机的作用就是用来做词法分析的,将字符流分解为词(token)。

2. 词(token)→ DOM树

接下来就需要将 Token 解析为 DOM 节点,并将 DOM 节点添加到 DOM 树中。这个过程是通过栈结构来实现的,这个栈主要用来计算节点之间的父子关系,上面步骤中生成的 token 会按顺序压入栈中,该过程的规则如下:

  • 如果分词器解析出来是StartTag Token,HTML 解析器会为该 Token 创建一个 DOM 节点,然后将该节点加入到 DOM 树中,它的父节点就是栈中相邻的那个元素生成的节点;
  • 如果分词器解析出来是 文本 Token,那么会生成一个文本节点,然后将该节点加入到 DOM 树中,文本 Token 是不需要压入到栈中,它的父节点就是当前栈顶 Token 所对应的 DOM 节点;
  • 如果分词器解析出来的是 EndTag Token,比如是 EndTag div,HTML 解析器会查看 Token 栈顶的元素是否是 StarTag div,如果是,就将 StartTag div从栈中弹出,表示该 div 元素解析完成。

通过分词器产生的新 Token 就这样不停地入栈和出栈,整个解析过程就这样一直持续下去,直到分词器将所有字节流分词完成。

下面来看看这的Token栈是如何工作的,有如下HTML结构:

开始时,HTML解析器会创建一个根为 document 的空的 DOM 结构,同时将 StartTag document 的Token压入栈中,然后再将解析出来的第一个 StartTag html 压入栈中,并创建一个 html 的DOM节点,添加到document上,这时Token栈和DOM树如下:

HTML解析阶段是如何生成DOM树和CSSOM树?

接下来body和div标签也会和上面的过程一样,进行入栈操作:

HTML解析阶段是如何生成DOM树和CSSOM树?

随后就会解析到 div标签中的文本Token,渲染引擎会为该 Token 创建一个文本节点,并将该 Token 添加到 DOM 中,它的父节点就是当前 Token 栈顶元素对应的节点:

HTML解析阶段是如何生成DOM树和CSSOM树?

接下来就是第一个EndTag div,这时 HTML 解析器会判断当前栈顶元素是否是 StartTag div,如果是,则从栈顶弹出 StartTag div,如下图所示:

HTML解析阶段是如何生成DOM树和CSSOM树?

再之后的过程就和上面类似了,最终的结果如下:

HTML解析阶段是如何生成DOM树和CSSOM树?

二、构建CSSOM树

上面已经基本了解了 DOM 的构建过程,但是这个 DOM 结构只包含节点,并不包含任何的样式信息。下面就来看看,浏览器是如何把 CSS 样式应用到 DOM 节点上的。

同样,浏览器也是无法直接理解 CSS 代码的,需要将其浏览器可以理解的 CSSOM 树。实际上,浏览器在构建 DOM 树的同时,如果样式也加载完成了,那么 CSSOM 树也会同步构建,CSSOM 树和 DOM 树类似,它主要有两个作用:

  • 提供给 JavaScript 操作样式的能力
  • 为渲染树的合成提供基础的样式信息

不过,CSSOM 树和 DOM 树是独立的两个数据结构,它们并没有一一对应关系,DOM 树描述的是 HTML 标签的层级关系,CSSOM 树描述的是选择器之间的层级关系。可以在浏览器的控制台,通过 document.styleSheets 命令来查看 CSSOM 树: HTML解析阶段是如何生成DOM树和CSSOM树? 那 CSS 样式的来源有哪些呢? HTML解析阶段是如何生成DOM树和CSSOM树? 可以看到,CSS 样式的来源主要有三种:

  • 通过 link 引用的外部 CSS 样式文件
  • <style>标签内的CSS样式
  • 元素的 style 属性内嵌的 CSS

在将 CSS 转化为树形对象之前,还需要将样式表中的属性值进行标准化处理,比如,当遇到以下 CSS 样式:

body { font-size: 2em }
p {color:blue;}
div {font-weight: bold}
div p {color:green;}
div {color:red; }

可以看到上面 CSS 中有很多属性值,比如 2em、blue、red、bold等,这些数值并不能被浏览器直接理解。所以,需要将所有值转化为浏览器渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。经过标准化的过程,上面的代码会变成这样:

body { font-size: 32px }
p {color: rgb(0, 0, 255);}
div {font-weight: 700}
div p {color: (0, 128, 0);}
div {color: (255, 0, 0); }

可以看到,2em 被解析成了32px,blue 被解析成了rgb(255, 0, 0),bold 被解析成 700。现在样式的属性已被标准化了,接下来就需要计算 DOM 树中每个节点的样式属性了,这就涉及到 CSS 的继承规则和层叠规则。

1. 样式继承

在 CSS 中存在样式的继承机制,CSS 继承就是每个 DOM 节点都包含有父节点的样式。比如在 HTML 上设置“font-size:20px;” 那么页面里基本所有的标签都可以继承到这个属性了。

在 CSS 中,有继承性的属性主要有以下几种:

  1. 字体系列属性
  • font-family:字体系列
  • font-weight:字体的粗细
  • font-size:字体的大小
  • font-style:字体的风格
  1. 文本系列属性
  • text-indent:文本缩进
  • text-align:文本水平对齐
  • line-height:行高
  • word-spacing:单词之间的间距
  • letter-spacing:中文或者字母之间的间距
  • color:文本颜色
  1. 元素可见性
  • visibility:控制元素显示隐藏
  1. 列表布局属性
  • list-style:列表风格,包括list-style-type、list-style-image等
  1. 光标属性
  • cursor:光标显示为何种形态

2. 样式层叠

样式计算过程中的第二个规则是样式层叠,层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在 CSS 处于核心地位,CSS 的全称“层叠样式表”正是强调了这一点。

总之,样式计算阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在计算过程 中需要遵守 CSS 的继承和层叠两个规则,这个阶段最终输出的内容是每个 DOM 节点的样 式,并被保存在 ComputedStyle 的结构内。

对于以下代码:

最终生成的 CSSOM 树大致如下: HTML解析阶段是如何生成DOM树和CSSOM树?

转载自:https://juejin.cn/post/7295681756743647243
评论
请登录