作了好几年前端了,最近想进个大厂,参加了阿里、百度、今日头条的面试,被完全的打击了,感受本身的基础不是很牢,因此萌发了重学前端的想法,向习觉得常的知识/技能,多问一个为何,多了解一下原理,或者源码javascript
今天咱们来聊聊html中资源加载和执行的顺序问题。php
作了几年前端了,但是当浏览器获取到html文件后怎么加载执行文件,这些问题你能回答吗css
为了搞清楚这些问题,咱们来作下试验,我用的是chrome浏览器。html
咱们先构造一个html文件,在head里面交替加载10个css文件和10个js文件,这几个文件地址是我用本地php服务建立的,都延迟3秒返回数据,以便咱们清楚观察效果前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="http://test.com/test/css1">
<script src="http://test.com/test/js1"></script>
<link rel="stylesheet" href="http://test.com/test/css2">
<script src="http://test.com/test/js2"></script>
<link rel="stylesheet" href="http://test.com/test/css3">
<script src="http://test.com/test/js3"></script>
<link rel="stylesheet" href="http://test.com/test/css4">
<script src="http://test.com/test/js4"></script>
<link rel="stylesheet" href="http://test.com/test/css5">
<script src="http://test.com/test/js5"></script>
<link rel="stylesheet" href="http://test.com/test/css6">
<script src="http://test.com/test/js6"></script>
<link rel="stylesheet" href="http://test.com/test/css7">
<script src="http://test.com/test/js7"></script>
<link rel="stylesheet" href="http://test.com/test/css8">
<script src="http://test.com/test/js8"></script>
<link rel="stylesheet" href="http://test.com/test/css9">
<script src="http://test.com/test/js9"></script>
<link rel="stylesheet" href="http://test.com/test/css10">
<script src="http://test.com/test/js10"></script>
</head>
<body>
</body>
</html>
复制代码
css和js的地址我是使用php框架构造的,目的是为了使用php的sleep延迟返回资源,好让咱们更清楚的看到时序,你能够经过node或其余后台自行构造java
//js文件,等待3s后返回
public function actionJs1(){
sleep(3);
return "console.log('js1111111')";
}
//css文件,等待3s返回
public function actionCss1(){
sleep(3);
}
复制代码
咱们经过chrome来看下加载时序,能够看到,浏览器首先下载html文件,下载以后紧接着按照文档顺序请求css一、js一、css二、js二、css三、js3,能够看到浏览器一次能够加载6个文件,加载完成以后再加载6个,直到所有加载完成。node
而加载顺序实际反复测试几回,均为【css一、js一、css二、js二、css三、js3】、【css四、css五、css六、css七、css八、css9】、【css十、js四、js五、js六、js七、js8】、【js九、js10】,能够看出,有两个特色:总体来讲是按照文档在html中出现顺序进行加载的,可是会部分优先加载css文件css3
为了证明上面的猜测,有对html代码进行了改造,10个css全放前面,10个js全放后面面试
<link rel="stylesheet" href="http://test.com/test/css1">
......
<link rel="stylesheet" href="http://test.com/test/css10">
<script src="http://test.com/test/js1"></script>
......
<script src="http://test.com/test/js10"></script>
复制代码
果真,浏览器优先把前面10个css先加载完毕,而后才加载js chrome
而若是咱们把10个js放到10个css文件前面呢
<script src="http://test.com/test/js1"></script>
......
<script src="http://test.com/test/js10"></script>
<link rel="stylesheet" href="http://test.com/test/css1">
......
<link rel="stylesheet" href="http://test.com/test/css10">
复制代码
能够看到,浏览器先加载了6个js,而后又优先把10个css加载完,以后才加载js
接下来在body中再添加10个图片,看看css、js、图片,这三种资源的加载顺序,不用测试咱们应该也能想到,图片应该在css和js以后,实验验证一下。
构造测试html,每一个资源设置6个,顺序以下
<link rel="stylesheet" href="http://test.com/test/css1">
......
<link rel="stylesheet" href="http://test.com/test/css6">
<img src="http://test.com/test/img1"/>
......
<img src="http://test.com/test/img6"/>
<script src="http://test.com/test/js1"></script>
......
<script src="http://test.com/test/js6"></script>
复制代码
chrome文件加载时序为,先加载6个css、再加载6个js,最后加载6个图片
浏览器每次只能加载6个文件,那还能不能再多加载一些呢,假如加载的资源域名不一样,会发生什么?
//前六个css域名为test.com
<link rel="stylesheet" href="http://test.com/test/css1">
......
<link rel="stylesheet" href="http://test.com/test/css6">
//后六个css域名为m.test.com
<link rel="stylesheet" href="http://m.test.com/test/css1">
......
<link rel="stylesheet" href="http://m.test.com/test/css6">
复制代码
能够看到,浏览器一次性加载了12个文件
1.浏览器加载文件,总体顺序是按照文件在html中出现的顺序进行加载
2.可是会优先加载css、而后加载js、最后加载图片
3.同一个域名的资源,谷歌浏览器每次加载6个,不一样域名的资源能够并行加载
看到这里,咱们应该会性能优化有了一些想法了吧,好比使用不一样域名能够提升总体加载速度
js会在加载成功当即执行吗,仍是会受到别的因素影响呢?咱们先来测试一下吧,假如css延迟3s返回,js取消延迟当即返回,看看会发生什么
//css延迟3s返回数据
<link rel="stylesheet" href="http://test.com/test/css1">
//js当即返回,console.log('js1')
<script src="http://test.com/test/js1"></script>
复制代码
刷新页面能够看到尽管js很快就加载完成了,可是并无当即执行,等了3s左右才执行,也就是在css加载完成后才执行,即css的加载会阻塞js的执行,那是否是由于css在js前面的缘由呢?
咱们调换一下css和js的顺序,则能够看到js当即打印,并不会等待css的加载
//js当即返回,console.log('js1')
<script src="http://test.com/test/js1"></script>
//css延迟3s返回数据
<link rel="stylesheet" href="http://test.com/test/css1">
复制代码
接下来咱们看看两个js的加载执行顺序,先从最普通的开始
//js1延迟5s返回数据,console.log('js1')
<script src="http://test.com/test/js1"></script>
//js2延迟3s返回数据,console.log('js2')
<script src="http://test.com/test/js2"></script>
<script> console.log('js3') </script>
复制代码
等待5s后,控制台前后打印 js1 js2 js3,虽然js2提早加载完成,可是仍然要等待前面的js1加载执行完毕才能执行
js异步加载执行有两种方式async和defer,接下来咱们给js添加async属性看看
//js1延迟5s返回数据,console.log('js1')
<script src="http://test.com/test/js1" async></script>
//js2延迟3s返回数据,console.log('js2')
<script src="http://test.com/test/js2" async></script>
<script> console.log('js3') </script>
复制代码
控制台当即打印顺序为
js3
js2
js1
复制代码
可见添加async属性js不会阻塞其后面js的执行,谁先加载完成谁先执行,接下来添加defer属性。
//js1延迟5s返回数据,console.log('js1')
<script src="http://test.com/test/js1" defer></script>
//js2延迟3s返回数据,console.log('js2')
<script src="http://test.com/test/js2" defer></script>
<script> console.log('js3') </script>
复制代码
控制台打印顺序以下,能够看出,添加defer属性的js,不会阻塞后面的js执行,可是多个添加defer的js,仍然按照既有顺序执行。
js3
js1
js2
复制代码
下面是一张经典js加载执行时机对比图,
浏览器遇到没有添加异步属性的js,会当即加载并执行,也就是说会阻塞html的解析
浏览器遇到添加async属性的js会当即加载(固然了,如谷歌浏览器,就算没有添加async,它也会提早识别html中的js文件进行加载,加载时机如第一部分讨论),并在js加载完毕以后当即执行;多个async属性的js谁先加载完成谁先执行
浏览器遇到添加defer属性的js会当即加载,可是不会当即执行,而是会在html解析完成以后,DOMContentLoaded触发以前执行;多个defer属性的js会按照文档顺序执行
上面提到添加defer属性的js会在文档解析以后,DOMContentLoaded触发以前执行,也就是说,在添加defer属性的js中咱们是能够获取到dom的
//js1延迟5s返回数据,console.log(document.getElementById('test'))
<script src="http://test.com/test/js1" defer></script>
<script> console.log(document.getElementById('test')) </script>
<div id="test"></div>
复制代码
内联js,在id=“test”的dom以前,因此输出null,而defer属性的js打印出了dom
null
<div id="test"></div>
复制代码
即在defer属性的js中,能够安全的操做dom
网上查看一些资料,通常说法是这样的
一、当 onload 事件触发时,页面上全部的DOM,样式表,脚本,图片,flash都已经加载完成了。
二、当 DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。
可是经过测试发现并不是如此,假设css延迟3s返回,图片延迟5s返回,咱们看看打印
//css延迟3s返回数据
<link rel="stylesheet" href="http://test.com/test/css1">
<script> document.addEventListener('DOMContentLoaded',function () { console.log('DOMContentLoaded') },false) window.onload=function () { console.log("onload") } </script>
//延迟5s返回数据
<img src="http://3w.com/test/img1"/>
复制代码
能够看出3s以后才打印DOMContentLoaded,5s以后打印onload,也就是说DOMContentLoaded是须要等待css加载完成的 咱们刚才前面说了,defer属性的js要在DOMContentLoaded以前执行,那么假如defer属性延迟3s返回呢,咱们看看效果
<script> document.addEventListener('DOMContentLoaded',function () { console.log('DOMContentLoaded') },false) window.onload=function () { console.log("onload") } </script>
//js1延迟3s返回数据
<script src="http://test.com/test/js1" defer></script>
//延迟5s返回数据
<img src="http://3w.com/test/img1"/>
复制代码
一样的也在3s以后打印DOMContentLoaded,5s以后打印onload,能够说网上的结论和实际是不相符的
一样的这些结论也给咱们作前端优化提供一些思路,好比给js添加async属性、减小js及css大小、使用懒加载减小图片请求,使其尽快进入onload事件等等
有兴趣的同窗欢迎关注公众号,让咱们一块儿重学前端,夯实基础。