DomcontentLoaded事件何时触发

152 阅读4分钟

DomcontentLoaded事件何时触发

HTML被完全加载及解析时,DomcontentLoaded事件会被触发

MDN的解释是当HTML被完全加载以及解析时,DOMContentLoaded 事件会被触发。那HTML被完全加载及解析又指的是什么呢,如果我们搞清楚这个概念就能清晰的知道DOMContentLoaded的触发时机。

我们先来看一下浏览器的渲染过程:

image.png

浏览器获取到html文件后就开始解析生成Dom对象,这里当Dom对象生成之后就表示HTML被加载及解析完成了。此外如果有css会根据css生成cssom对象,然后再由cssom和dom合并生成渲染树。有了渲染树便根据节点和样式计算出它们在浏览器中的大小和位置,这就是布局阶段,之后再把节点绘制到屏幕上,这样就完成了第一次渲染。

上面我们已经说了当DOM对象生成之后就表示HTML被加载及解析了,这个时候会触发DomcontentLoaded事件,如果html文档中没有script脚本时这个构建dom的流程很简单,而当包含script和css时情况就变得复杂起来,下面我们来看一下script和css是怎么影响Dom构建的。

script如何影响dom构建

内联脚本 & 同步脚本

<html>
  <head>
      // 内联脚本
    <script>
      console.log("Hello");
    </script>
  </head>
  
   <body>
     // 同步脚本
    <script src="http://www.example.com/example.js"></script>
    <script src="http://www.example.com/example2.js"></script>
   </body>
</html>

渲染进程在解析html文档时如果遇到内联脚本会直接执行,遇到外部同步脚本会先加载再执行,在此期间会停止dom构建,无论遇到内联或同步的外部脚本都秉承着由上至下串行执行的原则。所以当文档中包含脚本时,脚本会阻塞dom的构建。

defer 异步脚本

<html>
  <head>
    <script src="http://www.example.com/example.js" defer></script>
    <script src="http://www.example.com/example2.js" defer></script>
  </head>
  
   <body>
   </body>
</html>

渲染进程在遇到defer脚本时,会使用其它进程或线程异步加载脚本,渲染进程继续解析dom,当dom解析完成后再执行defer脚本,如果有多个defer脚本会按照它们在文档中的位置按顺序执行。所以defer脚本不影响domcontentloaded事件触发,它会在该事件触发后再执行。

async 异步脚本

<html>
  <head>
    <script src="http://www.example.com/example.js" async></script>
    <script src="http://www.example.com/example2.js" async></script>
  </head>
  
   <body>
   </body>
</html>

渲染进程在遇到async脚本时,会使用其它进程或线程异步加载脚本,当加载成功后直接执行,如果有多个async脚本则执行顺序不确定,在执行阶段会暂停dom解析。async有两种情况,如果加载成功时dom已解析完成,则不影响domcontentloaded触发时机,如果加载成功时dom还未解析完成,则会影响domcontentloaded的触发时机。

css如何影响dom构建

渲染进程在遇到link标签时,会另起线程异步加载构建cssom,所以css不会直接影响到dom构建。但考虑到脚本在执行时可能会读取或修改cssom,所以脚本在执行前会需要等待前面所有的样式表加载并构建成cssom后才会执行,因此link也会影响到dom构建。

  • link后无script:不影响dom构建
  • link后有同步script:影响dom构建
  • link后有defer script:不影响dom构建
  • link后有async script:取决于脚本的执行时机

总结

是否影响dom构建
同步script
defer script
async script可能影响,加载完成时若dom未构建完成,则影响,若dom已构建完成则不影响
link是否影响取决于其后是否有script及script的类型

我们可以试着从浏览器的实现角度去理解这个问题,渲染进程在解析构建dom的过程中是同步执行的,如果它遇到script是同步去加载执行的,肯定会暂停dom的构建,而如果它异步去加载,就不会影响dom的构建,所以我们可以从defer、async、style的加载执行是异步还是同步,以及它的执行时机来分析它是否会影响dom构建。defer、async的加载是异步的,执行都是同步的,defer是等到dom构建完成后再执行,async是加载完就执行。

补充defer、async的使用场景

defer

  • 非关键性脚本,不影响页面首屏渲染或用户初始视图的脚本,可以使用defer优化加载
  • 需要等dom解析完才执行,即需要访问或修改dom节点

async

  • async脚本执行没有顺序,适用于比较独立的脚本,脚本之间没有依赖关系,并且不依赖于特定的dom结构。

defer其实很像把script放在body底部的效果,区别在于它可以提前进行下载,不过现在的构建工具一般会使用link preload来实现脚本的预下载,所以效果基本一致,但是defer不会影响domcontentloaded事件的触发。

参考资料

zhuanlan.zhihu.com/p/25876048

juejin.cn/post/691789…