深入理解DOM树的加载过程
本文整理介绍有关DOM的加载过程。DOM树加载从用户在地址栏输入一个有效URL并回车开始,到整个页面请求渲染加载完成并展现出来截止,在这个过程中发生的诸多细节,将在下文中整理介绍。
简要流程
从用户在地址栏完成地址输入到页面展现的简要流程如下:
- 浏览器将URL交给DNS域名解析器进行解析,得到目标的IP地址,然后向服务建立TCP连接,发送HTTP请求;
- 服务器根据浏览器的请求地址和数据在后台进行处理,返回页面信息给浏览器;
- 浏览器接收页面数据,包括 js、html、css 和 图片、字体 等信息;
- 浏览器对页面的 html 和 css 构建DOM树和渲染树,并渲染页面,展示到前台;
- 浏览器解析接收到的 js 文件,注册响应的事件并绑定到DOM节点。
详细流程
DOM加载的详细流程可以设计 DNS域名解析、DNS缓存、资源缓存、DOM树构建,渲染树构建,和事件绑定等,可概括为:
- 用户在浏览器输地址栏输入地址后回车,发起请求;
- 浏览器通过URL识别本地缓存,查看目标资源是否在本地缓存且有效;
- 如果目标资源没有缓存,则发起缓存,见后续步骤;
- 如果资源以及缓存,则坚持缓存是否过期。如果没有过期,则使用缓存;如果资源过期,则发起请求,从服务器对资源进行重新验证;
- 缓存涉及到两个HTTP头,
Expires
和Catch-Control
; - HTTP 1.0 提供
Expires
,其值为一个绝对时间,表示缓存的过期时间; - HTTP 1.1 增加了
Catch-Control
,通过max-age=
来设置缓存最大保留时间
- 浏览器得到数据后,开始构建DOM树,以 document 为根节点,根据 html 标签依次在根节点逐层注册新节点;
- 遇到 link 标签引用 css 时,并行下载 css 文件;
- 遇到 style 标签时,解析标签内的样式表;
- 遇到 script 标签内有 js 代码时,开始执行 js 代码,此时 DOM 树的构建会被暂停,直到 js 解析完成;
- 如果遇到 script 标签时引用外部的 js 文件,则并行下载 js 文件,这里包含两种情况;
- 如果 script 标签标记了 async 属性,则并行下载 js 文件,下载完成后立即执行;
- 如果 script 标签标记了 defer 属性,则并行下载 js 文件,并按照 defer 的顺序先后执行;
- 浏览器在 document 上触发 DOMContentLoaded 事件;
- 此时整个文档解析渲染完成,但可能一些静态资源还在下载,如图片等,这些资源文件会在下载完后载入,这些异步的载入不会影响到 document.readState 。
- 当 document.readState 为 complete 时,表示DOM树构建完毕,此时在 window 上触发 load 事件,可通过 window.onload 定义该事件的监听
引申:script 标签的 async 和 defer 属性
整个成中有关 script 标签异步载入 Js 代码需要注意两个属性,即 async 和 defer ,这两个属性都表示异步载入 js 代码,即下载 js 时不会影响 DOM 树构建,但需要区别这两个属性的含义。这里整理如下:
- async,异步下载,尽快执行,执行时可以访问到其 script 标签以上的节点;
- defer,异步下载,延迟到整个页面解析完毕后并按顺序执行,执行时间为文档渲染完毕后,但在触发 DOMContentLoaded 事件之前,所以其标签所引用的代码可以访问整个页面节点。
引申:页面缓存机制
引申:document.readState 状态值
readyState
属性返回当前文档的状态,有下列枚举值:
- uninitialized:还未开始载入;
- loading: 载入中;
- interactive: 已加载,文档与用户可以开始交互;
- complete: 载入完成
使用 JavaScript 进行编程时,可以在 document
对象上监听 readystatechange
事件,以便获取文档状态,一般当 readyState
属性值变为 complete 后开始触发 window
的 load
事件。
// 解析中
console.log(document.readyState); // loading
// 载入完成后触发 window 的 load 事件
window.onload = function () {
console.log(document.readyState); // complete
}