文档解析和DOMContentLoaded触发时机

2022-06-29 15:09:14 浏览数 (1)

| 导语 大家都知道 HTML 文档完全加载和解析完成之后,会触发 DOMContentLoaded 事件,那么 HTML 里面的内容会如何影响文档解析呢?在解析过程中遇到 script 标签或者 link 标签时,解析会受影响吗?我们通过不同情况举例和 HTML5 规范一起分析一下。

DOMContentLoaded 触发定义

HTML 文档被完全加载和解析完成之后,会触发 DOMContentLoaded 事件,通常外部样式表和文档内的图片加载都不会影响该事件触发,不过也有特殊情况,我们后面会提到。

如果遇到 script 标签,会停止文档解析,去运行脚本,因为脚本有可能改动 DOM,比如一些 document.write 操作,但是设置了 async 属性的 script 标签不会影响事件触发。

下面具体分析一些场景下 DOMCOntentLoaded 触发时机。

script 标签

文档解析行为会根据 script 标签的不同属性设置而表现不同,

HTML5 规范里面关于 script 标签属性介绍的部分: https://html.spec.whatwg.org/multipage/scripting.html#attr-script-async

主要 async 和 defer 两个属性:

  1. async:当脚本可用时立刻执行,在请求资源的过程中不会阻塞解析,资源请求完成后,如果此时文档还没解析完成,就会阻塞解析。只有在脚本请求耗时比文档解析时间长的情况下,才不会影响
  2. defer:推迟脚本执行,保证不阻塞文档解析,意味着即使脚本从网络请求完成也不会立刻执行,只有等到文档解析完成后执行

它们属性值都是 boolean 类型,并且只有在 src 属性存在的情况下有效。

从规范里面看到的解析时间图看非常清楚,红线代表 script 脚本解析,绿色代表 html 解析,蓝色代表网络资源请求。

没设置属性的时候,script 标签在请求和执行的时候都会阻塞文档解析。defer 和 async 属性设置后,请求阶段不会影响解析,但是 async 的标签会立即执行,影响解析。后面 type="module" 的情况跟上面相同。

总结一下,文档里面 script 标签,在两种情况下不会影响文档解析:

  1. 设置了 defer 属性
  2. 设置了 async 属性,并且脚本请求完成时,文档已经解析完成了

外部样式表

样式表通常不会影响 html 文档解析。

在浏览器的工作原理文章里面,提到样式表理论上不会改变 DOM 树,因此 html 解析的时候不会等待样式表。

但是脚本在文档解析阶段去请求样式信息,此时还没有加载和解析样式,脚本就会得到错误的结果。所以是否影响文档解析这里要区分一下样式表的不同情况。

1. 样式表后面紧跟着 script 脚本

代码语言:javascript复制
<html lang=en>
 <head>
     <link rel="stylesheet" href="//8.idqqimg.com/edu/assets/css/common_css_e582a1d4.css">
     <script>console.log(121212)</script>
 </head>
 <body>
   <p>23233</p>
 </body>
</html>

从 chrome 开发者工具里面的 Performance 分析看:

蓝色竖线 DCL,表示触发 DOMContentLoaded 时刻, 也就是说在 css 加载完成后触发的。

蓝色 Parse Stylesheet 部分,表示解析前面下载完成的样式表。

黄色 Evaluate Script 部分,执行脚步,从下面的 Summary 可以定位到刚好是第 4 行执行 console.log。

这种情况下是影响了文档解析。

2. 样式表后面没有 script 脚本

代码语言:javascript复制
<html lang=en>
 <head>
     <script>console.log(121212)</script>
     <link rel="stylesheet" href="//8.idqqimg.com/edu/assets/css/common_css_e582a1d4.css">
 </head>
 <body>
   <p>23233</p>
 </body>
</html>

还是从 Performance 分析:

蓝色竖线DCL几乎与css网络请求同时进行的,可以肯定样式加载没有影响到文档解析。

总结一下,通过上面两种页面,在Performance工具里面的DCL指标线,可以非常明显的看出来,外部样式表在某种情况也会影响页面解析,后面我们从HTML5规范里面找到一些说明。

HTML5 规范

文档解析部分,直接看最后解析结束部分,https://html.spec.whatwg.org/multipage/parsing.html#the-end

到文档解析后面,主要看看几个步骤,到第三步:

第 3 步,update document readiness 主要更新当前文档的 readyState 值,触发 readystatechange 事件

第 5 步,遍历所有待执行的 script 列表并执行,这里提到了 has no style sheet that is blocking scripts,必须是没有阻塞脚本的样式表存在才会去执行 script。

第 6 步,才会去触发 DOMContentLoaded 事件。

再到前面 script 脚本执行部分,https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-incdata

到 otherwise 里面,脚本执行会阻止文档解析,如果脚本没有被设置 "ready to be parser-executed" 或者文档中存在阻止脚本执行的样式表,脚本执行也不会进行下去了。从这条解析路线看,的确会出现样式表阻止文档解析的情况。

但是,不一定会一直等待样式表加载。再到 a style sheet that is blocking scripts 具体定义部分:

https://html.spec.whatwg.org/multipage/semantics.html#interactions-of-styling-and-scripting

用户代理可以随时主动放弃加载样式表,提示部分提到了样式表改变元素颜色成绿色,但是如果脚本在样式表加载之前就去获取颜色,就会得到默认的黑色,影响整个页面效果。实现者必须要权衡脚本获取到错误的样式和在等待缓慢网络请求时没有执行任何解析的性能影响之间的平衡。当然这也可以是页面性能优化的一点。

最后

为什么要分析影响文档加载的因素呢?肯定是为了更好的优化页面加载性能。

分别从优化 Javascript 加载和 CSS 发送两个角度分析,进行优化,具体优化建议可以看下面链接文章。

  • 移除会阻止页面呈现的 JS
  • 优化 CSS 发送过程

参考文章

  • DOMContentLoaded and stylesheets
  • HTML Living Standard - Parsing HTML documents
  • 页面生命周期:DOMContentLoaded,load,beforeunload,unload
  • css 加载会造成阻塞吗
  • 你不知道的 DOMContentLoaded

紧追技术前沿,深挖专业领域

扫码关注我们吧!

0 人点赞