1.    什么是 DOMContentLoaded。打开 Chrome DevTools,切到 Network 面板,重新加载网页,得到如下截图:

标记 1 指向的蓝线以及标记 2 指向的蓝色字 “DOMContentLoaded:1.29s” 均表示 DOMContentLoaded 这个事件触发的时间,只不过表现形式不同而已。

    什么是 DOMContentLoaded

我们先来思考一个问题,如何衡量一个网页的加载速度?

有人说可以用网页加载完全的时间来衡量。我觉得这没有问题,但不够好。为什么呢?在日常生活中,很多时候因为网络原因我们并不需要等待网页上的所有内容都加载完毕,而是只需要加载主要内容就可以了,比如你打开这篇博客时,可能并不需要等所有图片都加载完成,而是看到博客的正文就可以正常阅读了。把上面的话提炼一下就是,用户有时候只需要在空白的网页上看见内容就可以了,而不需要等待所有内容都加载出来。那既然这样,回到刚刚的问题,我觉得衡量一个网页加载速度的一个方法就是“计算这个网页从空白到出现内容所花费的时间”。那怎么计算这段时间?HTML5 规范已经帮我们完成了相应的工作,就是当一个 HTML 文档被加载和解析完成后,DOMContentLoaded 事件便会被触发。

这时问题又来了,“HTML 文档被加载和解析完成”是什么意思呢?或者说,HTML 文档被加载和解析完成之前,浏览器做了哪些事情呢?那我们需要从浏览器渲染原理来谈谈。

浏览器向服务器请求到了 HTML 文档后便开始解析,产物是 DOM(文档对象模型),到这里 HTML 文档就被加载和解析完成了。如果有 CSS 的会根据 CSS 生成 CSSOM(CSS 对象模型),然后再由 DOM 和 CSSOM 合并产生渲染树。有了渲染树,知道了所有节点的样式,下面便根据这些节点以及样式计算它们在浏览器中确切的大小和位置,这就是布局阶段。有了以上这些信息,下面就把节点绘制到浏览器上。所有的过程如下图所示:

现在你可能了解 HTML 文档被加载和解析完成前浏览器大概做了哪些工作,但还没完,因为我们还没有考虑现在前端的主角之一 JavaScript。

JavaScript 可以阻塞 DOM 的生成,也就是说当浏览器在解析 HTML 文档时,如果遇到 <script>,便会停下对 HTML 文档的解析,转而去处理脚本。如果脚本是内联的,浏览器会先去执行这段内联的脚本,如果是外链的,那么先会去加载脚本,然后执行。在处理完脚本之后,浏览器便继续解析 HTML 文档。看下面的例子:

<body>
 <script type="text/javascript">
 console.log(document.getElementById('ele')); // null
 </script>

<div id="ele"></div>

<script type="text/javascript">
 console.log(document.getElementById('ele')); // <div id="ele"></div>
 </script>
</body>

另外,因为 JavaScript 可以查询任意对象的样式,所以意味着在 CSS 解析完成,也就是 CSSOM 生成之后,JavaScript 才可以被执行。

到这里,我们可以总结一下。当文档中没有脚本时,浏览器解析完文档便能触发 DOMContentLoaded 事件;如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等 CSSOM 构建完成才能执行。在任何情况下,DOMContentLoaded 的触发不需要等待图片等其他资源加载完成。

我们到这里一直在说同步脚本对网页渲染的影响,如果我们想让页面尽快显示,那我们可以使用异步脚本。HTML5 中定义了两个定义异步脚本的方法:defer 和 async。我们来看一看他们的区别。

同步脚本(标签中不含 async 或 defer): <script src="***.js" charset="utf-8"></script>

当 HTML 文档被解析时如果遇见(同步)脚本,则停止解析,先去加载脚本,然后执行,执行结束后继续解析 HTML 文档。过程如下图:

defer 脚本:<script src="***.js" charset="utf-8" defer></script>

当 HTML 文档被解析时如果遇见 defer 脚本,则在后台加载脚本,文档解析过程不中断,而等文档解析结束之后,defer 脚本执行。另外,defer 脚本的执行顺序与定义时的位置有关。过程如下图:

async 脚本:<script src="***.js" charset="utf-8" async></script>

当 HTML 文档被解析时如果遇见 async 脚本,则在后台加载脚本,文档解析过程不中断。脚本加载完成后,文档停止解析,脚本执行,执行结束后文档继续解析。过程如下图:

如果你 Google "async 和 defer 的区别",你可能会发现一堆类似上面的文章或图片,而在这里,我想跟你分享的是 async 和 defer 对 DOMContentLoaded 事件触发的影响。

defer 与 DOMContentLoaded

如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行。而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。 所以这意味着什么呢?HTML 文档解析不受影响,等 DOM 构建完成之后 defer 脚本执行,但脚本执行之前需要等待 CSSOM 构建完成。在 DOM、CSSOM 构建完毕,defer 脚本执行完成之后,DOMContentLoaded 事件触发。

async 与 DOMContentLoaded

如果 script 标签中包含 async,则 HTML 文档构建不受影响,解析完毕后,DOMContentLoaded 触发,而不需要等待 async 脚本执行、样式表加载等等。

   DOMContentLoaded 与 load

与标记 1 的蓝线平行的还有一条红线,红线就代表 load 事件触发的时间,对应的,在最下面的概览部分,还有一个用红色标记的 "Load:1.60s",描述了 load 事件触发的具体时间。

这两个事件有啥区别呢?点击这个网页你就能明白:https://testdrive-archive.azu...

解释一下,当 HTML 文档解析完成就会触发 DOMContentLoaded,而所有资源加载完成之后,load 事件才会被触发。

另外需要提一下的是,我们在 jQuery 中经常使用的 $(document).ready(function() { // ...代码... }); 其实监听的就是 DOMContentLoaded 事件,而 $(document).load(function() { // ...代码... }); 监听的是 load 事件。

对DOMContentLoaded的研究 -----------------------引用的更多相关文章

  1. 对Webpack 应用的研究-----------------引用

    对大多数 Web 应用来说,页面性能直接影响着流量.这是一个经常为我们所忽视的事实.用户长时间的等待流失的不仅仅是跳出率.转化率,还有对产品的耐心和信赖.很多时候我们没有意识到性能问题,那是因为平常开 ...

  2. 对JS继承的研究--------------引用

    问:类继承和原型继承不是同一回事儿吗,只是风格选择而已? 答:不是! 类继承和原型继承不论从本质上还是从语法上来说,都是两个截然不同的概念. 二者之间有着区分彼此的本质性特征.要完全看懂本文,你必须牢 ...

  3. 对React性能优化的研究-----------------引用

    JSX的背后 这个过程一般在前端会称为“转译”,但其实“汇编”将是一个更精确的术语. React开发人员敦促你在编写组件时使用一种称为JSX的语法,混合了HTML和JavaScript.但浏览器对JS ...

  4. 对vue-router的研究--------------引用

    pushState/replaceState/popstate 解析 HTML5提供了对history栈中内容的操作.通过history.pushState/replaceState实现添加地址到hi ...

  5. 对ECMAScript的研究-----------引用

    ECMAScript 新特性与标准提案 一:ES 模块 第一个要介绍的 ES 模块,由于历史上 JavaScript 没有提供模块系统,在远古时期我们常用多个 script 标签将代码进行人工隔离.但 ...

  6. 对GraphQL-BFF:微服务背景下的前后端数据交互方案的研究-------引用

    随着多终端.多平台.多业务形态.多技术选型等各方面的发展,前后端的数据交互,日益复杂. 同一份数据,可能以多种不同的形态和结构,在多种场景下被消费. 在理想情况下,这些复杂性可以全部由后端承担.前端只 ...

  7. 对webview的研究--------引用

    简要的说,webview能够在移动应用中开辟出一个窗口,在里面显示html页面,css以及js代码也可以被解析执行,它使用的是我们熟悉的webkit内核.android和ios都有相应的API,所以写 ...

  8. 对React的研究-------------引用

    DOM 的全称是 Document Object Model (文档对象模型) 响应式 UI const ThinkerWithHat = ({ hat }) => (  <div> ...

  9. 对组件、Prop 和 State的研究-----------------引用

    组件 第一步是将 UI 分解成多个组件.例如,我们可以这样来拆分房子: 现在来编码! House:  <div>    <Roof />     // 房顶    <Wa ...

随机推荐

  1. CSRF token的原理

    参考: http://www.cnblogs.com/zhaof/p/6281482.html 简介 django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.cs ...

  2. Centos7的引导顺序

    1.UEFI或BIOS初始化,运行POST开机自检(Power   On  Self   Test) 2.选择启动设备 3.引导装载程序grub2 4.加载装载程序的配置文件:/etc/grub.d/ ...

  3. 《Python编程从0到1》笔记5——图解递归你肯定看完就能懂!

    本小节的示例比较简单,因为在每次递归过程中原问题仅缩减为单个更小的问题.这样的问题往往能够用简单循环解决.这类递归算法的函数调用图是链状结构.这种递归类型被称为“单重递归”(single recurs ...

  4. python基础--面向对象初始

    # 类与对象,类是类别.种类,是面向对象设计中最重要的概念, # 对象是特征与技能的结合体, # 类是一系列对象相似特征与技能的结合体 # 例如:人是一个类,而我本人是一个对象,手,脚,是我的特征, ...

  5. 关于Docx动态控制word模板文件的数据

    博客:https://www.cnblogs.com/24klr/ github: https://github.com/luoruiemail/Dynamic_Word_Web 参考资料:https ...

  6. Spring MVC 中使用AOP 进行事务管理--注解实现

    注解实现实现事务管理很简单,和配置式差不多,配置文件的头文件也要加相应的支持.配置数据源,并开启事务管理支持即可. <bean id="transactionManager" ...

  7. js 获取ip和城市

    <script src="http://pv.sohu.com/cityjson?ie=utf-8"></script>

  8. x系统清理/tmp/文件夹的原理

    转自:http://www.opsers.org/base/clean-up-on-the-linux-system-tmp-folder-you-may-want-to-know.html§ 我们知 ...

  9. ABAP JSON转换

    REPORT ztest_json. DATA: json_ser TYPE REF TO cl_trex_json_serializer, json_des TYPE REF TO zcl_trex ...

  10. iOS-NSLog发布时取消打印日志

    1 选择工程的Target -> Build Settings -> Preprocessor Macros. 如图,默认 Debug项,是“DEBUG=1”. 2 在程序中设置全局宏定义 ...