本文首发在github,感兴趣请点击此处javascript
window 的 onload 事件对于前端童鞋来讲确定是熟的不能再熟了,相信你们在刚入门时,见的最多的可能就是 load 事件了。load 事件接触多了,你们就会接触到它的闺蜜 DOMContentLoaded 事件,网上有不少介绍这两个事件的文章,对们它的解释无外乎如下两种css
MDN的解释:load 应该仅用于检测一个彻底加载的页面 当一个资源及其依赖资源已完成加载时,将触发load事件。html
意思是页面的html、css、js、图片等资源都已经加载完以后才会触发 load 事件。前端
MDN的解释:当初始的 HTML 文档被彻底加载和解析完成以后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。java
意思是HTML下载、解析完毕以后就触发。git
看了这两个解释,我仍然一脸懵逼,只是像小学生背课文同样知道 load 和 DOMContentLoaded 事件的触发时机,但仍是不明白究竟什么状况下触发这两种事件。github
这两个词语表达的是一个意思,就是浏览器将资源下载到本地的过程。chrome
解析的意思是将一个元素经过必定的方式转换成另外一种形式。 好比 html 的解析。首先要明确,html 下载到浏览器的表现形式就是 包含字符串的文件。浏览器将 html 文件里面的字符串读取到内存中,按照 html 规则,对字符串进行取词编译,将字符串转化成另外一种易于表达的数据结构。咱们看下一段代码:浏览器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>只有css</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<div id="div1"></div>
<link rel="stylesheet" href="./c1.css" />
<link rel="stylesheet" href="./c3.css" />
<script src="http://test.com:9000/mine/load/case2/j1.js "></script>
<link rel="stylesheet" href="./c4.css" />
<div id="div2"></div>
</body>
</html>
复制代码
浏览器会对这个 html 文件进行编译,转化成相似下面的结构(这里把 head 中的其余标签省略了)。数据结构
浏览器会对转化后的数据结构自上而下进行分析:首先开启下载线程,对全部的资源进行优先级排序下载(注意,这里仅仅是下载)。同时主线程会对文档进行解析:
有一点要注意的是,在 body 中第一个 script 资源下载完成以前,浏览器会进行首次渲染,将该 script 标签前面的 DOM 树和 CSSOM 合并成一棵 Render 树,渲染到页面中。这是页面从白屏到首次渲染的时间节点,比较关键。
DOM 构建的意思是,将文档中的全部 DOM 元素构建成一个树型结构。
注意,DOM 构建是自上而下进行构建的,会受到 js 执行的干扰。
将文档中的全部 css 资源合并。
将 DOM 树和 CSS 合并成一棵渲染树,render 树在合适的时机会被渲染到页面中。(好比遇到 script 时, 该 script 尚未下载到本地时)。
当咱们输入一个页面地址时,发生了哪些事情呢?
body 里的状况比较多,body 里可能只有 DOM 元素,可能既有 DOM、也有 css、js 等资源,js 资源又有可能异步加载 图片、css、js 等。DOM 结构不一样,浏览器的解析机制也不一样,咱们分开来讨论。
测试代码以下
<body>
<!-- 白屏 -->
<div id="div1"></div>
<!-- 白屏 -->
<link rel="stylesheet" href="./c1.css" />
<!-- 白屏 -->
<link rel="stylesheet" href="./c3.css" />
<!-- 若是此时 j1.js 还没有下载到本地,则首次渲染,此时的 DOM 树 只有 div1 ,因此页面上只会显示 div1,样式是 c1.css 和 c3.css 的并集。-->
<!-- 若是此时 j1.js 已经下载到本地,则先执行 j1.js,页面不会渲染,因此此时仍然是白屏。-->
<!--下面的 js 阻塞了 DOM 树的构建,因此下面的 div2 没有在文档的 DOM 树中。 -->
<script src="http://test.com:9000/mine/load/case2/j1.js "></script>
<!-- j1.js 执行完毕,继续 DOM 解析,div2 被构建在文档 DOM 树中,此时页面上有了div2 元素,样式仍然是 c1.css 和 c3.css 的并集 -->
<link rel="stylesheet" href="./c4.css" />
<!-- c4.css 加载完毕,从新构建render树,样式变成了 c1.css、c3.css 和 c4.css 的并集 -->
<div id="div2"></div>
<script> // 利用 performance 统计 load 加载时间。 window.onload=function(){console.log(performance.timing.loadEventStart - performance.timing.fetchStart);} </script>
</body>
复制代码
你们能够调整资源摆放位置,观察浏览器的解析表现。
上面只是讲了 html 文档的加载与渲染,并无讲 DOMContentLoaded 事件的触发时机。直截了当地结论是,DOMContentLoaded 事件在 html文档加载完毕,而且 html 所引用的内联 js、以及外链 js 的同步代码都执行完毕后触发。
你们能够本身写一下测试代码,分别引用内联 js 和外链 js 进行测试。
当页面 DOM 结构中的 js、css、图片,以及 js 异步加载的 js、css 、图片都加载完成以后,才会触发 load 事件。
注意:
你们能够在 chrome 中试一下。
这里要注意关键词:同一域名。若是 n 个不一样域名的话,在浏览器设置的最大并发上限之内(默认是10个),是能够达到 n * 6 个的最大并发的下载的。
另外,load 事件与 DOMContentLoaded 事件触发所花费的时间,能够利用 performance 这个对象的一些属性进行统计,时间精确到纳秒级。一些大公司的性能统计也主要利用这个对象的数据进行上报。
还有一些其余的性能统计属性,你们能够研究下。
以上内容但愿能给你们带来不同的思考,并但愿能帮到你们理解文档的加载机理。