重学前端:作了这么多年前端,你真的了解html中资源加载执行时序吗?

作了好几年前端了,最近想进个大厂,参加了阿里、百度、今日头条的面试,被完全的打击了,感受本身的基础不是很牢,因此萌发了重学前端的想法,向习觉得常的知识/技能,多问一个为何,多了解一下原理,或者源码javascript

今天咱们来聊聊html中资源加载和执行的顺序问题。php

作了几年前端了,但是当浏览器获取到html文件后怎么加载执行文件,这些问题你能回答吗css

  • 按照什么顺序加载文件?
  • 每次能加载多少个文件?
  • css没加载完会执行js吗?
  • async和defer的区别是什么?
  • DOMContentLoaded事件和onload事件,什么时间触发?

为了搞清楚这些问题,咱们来作下试验,我用的是chrome浏览器。html

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个,不一样域名的资源能够并行加载

看到这里,咱们应该会性能优化有了一些想法了吧,好比使用不一样域名能够提升总体加载速度

html中js执行时机

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">
复制代码

结论

  1. 若是css在js前面,则css的加载会阻塞js的执行,即css加载完毕后,执行js
  2. 若是css在js后面,则不会阻塞js的执行

接下来咱们看看两个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

结论

  1. 若是css在js前面,则css的加载会阻塞js的执行,即css加载完毕后,执行js
  2. 若是css在js后面,则不会阻塞js的执行
  3. 非异步js按照顺序进行执行
  4. 多个async属性的js,谁先加载完成谁执行
  5. defer属性的js在文档解析以后,DOMContentLoaded触发以前执行,多个defer属性的js按文档顺序执行

DOMContentLoaded和onload

网上查看一些资料,通常说法是这样的

一、当 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,能够说网上的结论和实际是不相符的

结论

  1. 当 页面上全部的DOM,样式表,脚本,图片,flash都加载完成以后触发onload。
  2. 当DOM加载解析完成、css加载完成、内联js执行完成、defer属性的js完成才会触发DOMContentLoaded,图片和async属性的js,不会阻止发DOMContentLoaded的加载。

一样的这些结论也给咱们作前端优化提供一些思路,好比给js添加async属性、减小js及css大小、使用懒加载减小图片请求,使其尽快进入onload事件等等

有兴趣的同窗欢迎关注公众号,让咱们一块儿重学前端,夯实基础。

相关文章
相关标签/搜索