13-2 JS如何影响DOM树的构建

Posted by CodingWithAlice on April 11, 2021

13-2 JS如何影响DOM树的构建

什么是DOM?

渲染引擎无法直接识别网络进程传递的 HTML 字节流,而 DOM 就是能理解的结构

  • DOM的作用:

    1、页面 – DOM是生成页面的 基础数据结构

    2、JS脚本 – DOM提供了 JS脚本操作的接口,JS可以修改 DOM 结构、样式、内容

    3、安全 – 不安全的内容 在 DOM 解析阶段就被拒了

DOM树如何生成

渲染引擎内有一个HTML解析器HTML Parser):将HTML字节流转换为DOM结构(并不是等到整个文档加载完之后再开始解析,而是 网络进程加载了多少数据就解析多少 – 网络进程和渲染进程之间的管道)

HTML解析器做了什么

以下方字节流为例

image-20210414213421072

1、通过 分词器 将字节流转换为 Token

Token 分为 Tag Token 和文本 Token,其中Tag Token还分为StartTagEndTag

image-20210414213451895

2、将 Token 解析为 DOM 节点,并将 DOM 节点添加到 DOM 树中

  • 关于父子节点的判定:

    HTML 解析器维护了一个 Token 栈结构,该 Token 栈主要用来计算节点之间的父子关系,第一阶段中生成的Token依次被压入栈中(文本 Token 不需要压入到栈中)

  • 举例看下

    image-20210414213526880

JS在生成DOM树期间的影响

-总结:所以渲染引擎在 遇到 JavaScript 脚本 时,不管该脚本是否操纵了 CSSOM,都会先执行 CSS 文件下载,解析操作,再执行 JavaScript 脚本

1/3、HTML字节流中内嵌<script>标签

<html>
<body>
    <div>1</div>
    <script>
    let div1 = document.getElementsByTagName('div')[0]
    div1.innerText = 'time.geekbang'
    </script>
    <div>test</div>
</body>
</html>
  • 和正常仅HTML字节流区别:

    解析到 script 标签时,渲染引擎判断这是一段脚本,此时 HTML 解析器就会 暂停 DOM 的解析,因为接下来的JS可能要修改当前已经生成的 DOM 结构

image-20210414213620767

2/3、在页面中引入JS文件

image-20210414213702040

  • 和上方解析过程的差异:

其整个执行流程还是一样的,执行到 script 标签时, 暂停 DOM 的解析,执行JS代码,不过这里执行JS 时,需要先下载这段JS代码JS文件的下载过程会阻塞 DOM 解析

由于js文件的下载受到网络环境、js文件大小等多种因素的影响,所以Chrome也做了很多优化,其中最主要的就是 预解析操作【预解析操作:当 渲染进程收到字节流之后,会开启一个预解析线程,用来分析 HTML 文件中包含的 JavaScript、CSS 等相关文件,解析到相关文件之后,预解析线程会 提前下载 这些文件】

3/3、在js文件中出现CSS相关操作

<html>
    <head>
        <style src='theme.css'></style>
    </head>
    <body>
        <div>1</div>
        <script>
                let div1 = document.getElementsByTagName('div')[0]
                div1.innerText = 'time.geekbang' // 需要 DOM
                div1.style.color = 'red'  // 需要 CSSOM
            </script>
        <div>test</div>
    </body>
</html>

JS 代码出现了 div1.style.color = 'red' 的语句,它是用来操纵 CSSOM 的,所以在执行 JavaScript 之前,需要先解析 JavaScript 语句之上所有的 CSS 样式。所以如果代码里引用了外部的 CSS 文件,那么在执行 JavaScript 之前,还需要 等待外部的 CSS 文件下载完成,并解析生成 CSSOM 对象之后,才能执行 JavaScript 脚本