对DOMContentLoaded的研究 -----------------------引用
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的研究 -----------------------引用的更多相关文章
- 对Webpack 应用的研究-----------------引用
对大多数 Web 应用来说,页面性能直接影响着流量.这是一个经常为我们所忽视的事实.用户长时间的等待流失的不仅仅是跳出率.转化率,还有对产品的耐心和信赖.很多时候我们没有意识到性能问题,那是因为平常开 ...
- 对JS继承的研究--------------引用
问:类继承和原型继承不是同一回事儿吗,只是风格选择而已? 答:不是! 类继承和原型继承不论从本质上还是从语法上来说,都是两个截然不同的概念. 二者之间有着区分彼此的本质性特征.要完全看懂本文,你必须牢 ...
- 对React性能优化的研究-----------------引用
JSX的背后 这个过程一般在前端会称为“转译”,但其实“汇编”将是一个更精确的术语. React开发人员敦促你在编写组件时使用一种称为JSX的语法,混合了HTML和JavaScript.但浏览器对JS ...
- 对vue-router的研究--------------引用
pushState/replaceState/popstate 解析 HTML5提供了对history栈中内容的操作.通过history.pushState/replaceState实现添加地址到hi ...
- 对ECMAScript的研究-----------引用
ECMAScript 新特性与标准提案 一:ES 模块 第一个要介绍的 ES 模块,由于历史上 JavaScript 没有提供模块系统,在远古时期我们常用多个 script 标签将代码进行人工隔离.但 ...
- 对GraphQL-BFF:微服务背景下的前后端数据交互方案的研究-------引用
随着多终端.多平台.多业务形态.多技术选型等各方面的发展,前后端的数据交互,日益复杂. 同一份数据,可能以多种不同的形态和结构,在多种场景下被消费. 在理想情况下,这些复杂性可以全部由后端承担.前端只 ...
- 对webview的研究--------引用
简要的说,webview能够在移动应用中开辟出一个窗口,在里面显示html页面,css以及js代码也可以被解析执行,它使用的是我们熟悉的webkit内核.android和ios都有相应的API,所以写 ...
- 对React的研究-------------引用
DOM 的全称是 Document Object Model (文档对象模型) 响应式 UI const ThinkerWithHat = ({ hat }) => ( <div> ...
- 对组件、Prop 和 State的研究-----------------引用
组件 第一步是将 UI 分解成多个组件.例如,我们可以这样来拆分房子: 现在来编码! House: <div> <Roof /> // 房顶 <Wa ...
随机推荐
- 将JSON字符串反序列化为指定的.NET对象类型
目录导航: 前言: 方法一.在项目中定义对应的对象参数模型,用于映射反序列化出来的参数(复杂JSON字符串数据推荐使用): 方法二.直接将JSON字符串格式数据反序列化转化为字典数据(简单JSON字符 ...
- 【HANA系列】SAP HANA使用XS和HTTP创建proxy
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA使用XS和HTT ...
- LeetCode.1030-曼哈顿距离排序矩阵单元格(Matrix Cells in Distance Order)
这是小川的第384次更新,第412篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第246题(顺位题号是1030).我们给出一个矩阵,其中R行和C列具有整数坐标(r,c)的 ...
- LeetCode.1029-两城调度(Two City Scheduling)
这是小川的第383次更新,第412篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第245题(顺位题号是1029).公司计划采访的人数为2N.将第i个人飞往城市A的费用是[ ...
- Metinfo5.1 /about/show.php SQL注入
- 【神经网络与深度学习】Caffe训练执行时爆出的Check failed: registry.count(t ype) == 1 (0 vs. 1) Unknown layer type
自己建立一个工程,希望调用libcaffe.lib ,各种配置好,也能成功编译,但是运行就会遇到报错 F0519 14:54:12.494139 14504 layer_factory.hpp:77] ...
- 【VS开发】DLL和ocx的区别
ActiveX,OLE是基于COM的一种应用,其文件后缀一般以dll和ocx结尾:ocx作为一种特殊的dll文件,具有一定的用户界面和事件响应,而dll文件只是方法和属性的集合. 一.关于DLL的介绍 ...
- HDU 1160 FatMouse's Speed (动态规划、最长下降子序列)
FatMouse's Speed Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- 2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 H. Skiing
题意:在这个寒假中,鲍勃有计划在山区度假胜地滑雪.这个滑雪胜地有M个不同的滑雪道和N个不同的标志位于那些转弯处点.从第S标记到第T标志的第i路径具有长度L.每个路径必须遵循降低高度的原则,起点必须严格 ...
- 自己动手实现一个html2canvas
前言 昨天写了新手引导动画的4种实现方式,里面用到了 html2canvas 于是就顺便了解了一下实现思路. 大概就是 利用 svg 的 foreignObject 标签, 嵌入 dom, 最后再利用 ...