<script>标签的defer和async

        其实本来想说一下浏览器从接受到一个HTML文件到把它渲染出来这一整个流程的,但是网上已经有很好的文章了,这里就不在赘述,本篇文章主要举例几个我发现和理论不相符的点。阅读本文之前最好看一下下面推荐的文章。

浏览器渲染阅读推荐:

深入理解浏览器解析渲染HTML - 掘金 (juejin.cn)

浏览器解析渲染HTML文档的过程 - SegmentFault 思否

了解html页面的渲染过程 - yuezk - 博客园 (cnblogs.com)

再谈DOMContentLoaded与渲染阻塞—分析html页面事件与资源加载 - 知乎 (zhihu.com)

        在说defer和async之前,先来说一下没有这两个属性的<script>标签在HTML解析中的运行情况,很多的文章都说了无论内嵌的JS代码还是通过src外联的JS代码的解析和执行都会阻塞HTML文件的解析(DOM树的构建)和渲染树的构建,外联的JS的阻塞时间还会多一个下载资源的时间。问题在于什么时候开始解析、执行这些JS代码?

先来看看内嵌的JS代码:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <div>
        first div
    </div>
    <div>
        second div
    </div>
    <script>
        function heavyWork() {
            for(let i=0;i<10e5;i++){
                
            }
        }
        heavyWork()
    </script>
</body>
</html>

        这个内联的JS代码会在HTML解析到<script>的时候开始解析然后执行 ,不过值得注意的是浏览器把内嵌JS代码的解析和执行也算到解析HTML里面了,这光看图会以为解析HTML和解析执行JS并行了,JS的执行看上去没有阻塞HTML解析,其实不是,如图:

 再来看外联的JS代码,代码如下(out.js和上面内嵌的代码是一样的):

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <div>
        first div
    </div>
    <div>
        second div
    </div>
    <script src="./out.js"></script>
</body>
</html>

        这里如果按照网上的说法,过程应该是:从上到下先解析HTML代码,解析到<script>标签,浏览器另起一条线程去获取JS代码,并且阻塞HTML文件的解析,就像这样:

 

        但是用Chrome的Performance去看会发现并不是这样,具体看图:

         在JS代码执行的后面还有一小段解析HTML的过程,我截图没截进去,不过不重要,这图片反应出来一点和网上说的很不一样,那就是JS代码下载的起点时间,如果按照前面说的,JS文件的下载应该在HTML解析到一半的时候进行下载,并阻塞HTML的解析,但是这里下载的开始时间明显不对,而且这里的阻塞也不够彻底,JS代码的下载和HTML的解析居然存在并行的情况,那这又是问什么呢?这里就要说到“预解析”:

WebKit 和 Firefox 都进行了这项优化。在执行脚本时,其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。

        但是,这里说预解析是在“执行脚本”时候发生的,这与我们这里的情况也不是很符合,所以我猜测,与解析可能也发生在HTML代码的下载过程中,浏览器也会对下载下来的部分HTML代码进行预解析。这里的HTML解析被阻塞了是因为解析到对应的<script>标签发现JS文件还没下载好,所以阻塞了。

        上面是一个外联<script>,那么两个外联<script>的情况会是怎么样的呢?

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test</title>
</head>

<body>
    <div>12132</div>
<script src="./hhn.js"></script>//执行heavyWork1
<script src="./hhn2.js"></script>//执行heavyWork2
</body>
</html>

直接上图:

         这里和上面的情况其实差不多,这里明明是一个外联的<script>,却出现了内嵌<script>的情况,也就是看上去第二个脚本的解析、运行和HTML的解析是并行的,但是这不是真的并行,前面也提到过了。那么如何解释这种情况呢,最有可能就是第一个JS的脚本执行完后,第二个脚本还在下载,阻塞了HTML的解析,当HTML回复解析的时候,第二个脚本已经下载好了,浏览器把这个下载好的JS当作了内嵌的<script>来执行,也就是第二段JS代码的解析和执行被算到了HTML解析的过程里。这是我对这种情况的一个推测,如果大家有什么更好的解释,或者是官方的解释,欢迎到评论区留言。

言归正传,前面讲了这么多一般<script>的加载,现在来讲讲有defer和async属性的<script>

先来《Javascript高级程序设计》的解释

        defer:

         async:

         总结一下就是:如果在解析HTML的时候碰到了<script defer>,则浏览器继续解析HTML代码,同时下载JS代码,当HTML解析完后,若JS代码下载好了就执行,还没下载好就等下载好了再执行,若遇到了多个<script defer>也是不会阻塞HTML解析的,在解析完成后,在按照<script defer>的顺序来逐个执行下载下来的JS代码。如果浏览器在解析HTML的时候碰到了<script async>,与defer相同的是,这个下载这个代码是不会阻塞HTML的解析的,但是一旦这个JS代码下载好了(HTML还没解析完的话),这个下载好的JS代码运行,并且阻塞HTML的解析,而且如果遇到i多个<script async>标签,下载下来的多个JS代码的执行顺序是不按顺序执行的,哪一个下载好了就先执行。

来几张网图:

async有两种情况: 

 

         上面的图虽然很直观,但是是没有考虑到预加载的,有预加载的情况会复杂一些,先来看defer的情况,代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test</title>
</head>

<body>
    <div>12132</div>
<script defer src="./hhn.js"></script>
<script defer src="./hhn2.js"></script>
</body>
</html>

 

         这里的HTML页面一次性被解析完,但是解析完后JS文件还没下载好,中间就空了一段时间,被阻塞了,这里因为预加载,所以和上面的图例有点不一样。

        再来看看async的情况:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test</title>
</head>

<body>
    <div>12132</div>
<script async src="./hhn.js"></script>
<script async src="./hhn2.js"></script>
</script>
</body>
</html>

        

        这里因为我测试的HTML文件太小了,一下子就解析完了,所以不能体现出脚本下载好后,打断HTML解析这一现象, 而且两个JS文件也还是按顺序执行的,可能是这两个文件大小差不多的原因,第一个JS文件如果很大,第二个JS文件里的代码可能就会先运行,这里就不测试了。

        最后希望大家能多多找出我的错误,因为里面有一些我的推测,大家一起进步!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值