美文网首页javascript
JS的同步和异步加载

JS的同步和异步加载

作者: 张晖 | 来源:发表于2020-11-03 00:51 被阅读0次

    引言

    JS的“加载”不能理解为下载,它是分为两个部分:下载,执行。默认的JS加载是同步的,因为浏览器需要一个稳定的DOM结构,而执行JS时可能会对DOM造成改变,所以在执行JS时一定会阻塞HTML的渲染。我们可以使用一些方法使JS的下载不会阻塞HTML渲染,但不能使JS的执行阻塞渲染。

    以下从几个方面解释JS的加载:

    1、同步加载  
    2、异步加载
      2.1、Script DOM Element
      2.2、onload 时的异步加载

    1、同步加载

    同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像)、渲染、代码执行。使用方式如下:

    <script src="http://domin1.com/script1.js"></script>
    <script type="text/javascript">
      console.log('hello world');
    </script>
    

    这段代码中,如果请求的 script1.js 加载时间过长(domin1 被墙等原因),浏览器就会停止后续的文件下载,渲染,代码执行,第二个<script>中的"hello world"也无法显示。

    以前的处理方法是把<script>放置于页面末尾的</body>之前,可以使页面先显示出来,但这也只是让页面渲染不被阻塞而已,对于上例第二个 <script> 的 JS 执行所造成的阻塞是无能为力的。

    2、异步加载

    异步加载又叫非阻塞,浏览器在下载 JS 同时,还会继续进行后续页面的处理,上例第二个<script>的JS执行便不会被阻塞。

    2.1、Script DOM Element 法

    此方法不要求JS同源,使用方式如下:

    (function (){
        var async_script = document.createElement('script');
        async_script.type = "text/javascript";
        async_script.src = "http://domin.com/script.js";
        var x = document.getElementsByTagName("script")[0];
        x.ParentNode.insertBefore(async_script, x);
    })();
    

    以上代码使用 JS 创建了一个<script>插入到 document 中,便实现了异步加载 JS。
    将JS代码包裹在匿名函数中并立即执行的方式是为了保护变量名不被泄露到外部可见。

    但问题还是存在:这段代码在下载和执行完之前会阻止 onload 事件的触发,于是有了onload 时的异步加载。

    2.2、onload 时的异步加载
    (function() {
         function async_load(){
             var as = document.createElement("script");
             as.type = "text/javascript";
             as.src = "http://domain1.com/script.js";
             var x = document.getElementsByTagName("script")[0];
             x.parentNode.insertBefore(s, x);
         }
         if (window.attachEvent)
             window.attachEvent('onload', async_load);
         else
             window.addEventListener('load', async_load, false);
     })();
    

    两段代码的区别在于这段不是马上异步加载 JS ,而是在 onload 开始时才异步加载,便不会阻塞 onload 事件触发。

    2.3、async 和 defer 属性
    //async属性:
    <script src="script1.js" async="async"></script> 
    
    //defer属性:
    <script src="script2.js" defer="defer"></script> 
    

    async 属性使加载和渲染后续文档元素的过程将和当前 js 的下载与执行并行进行(异步);
    defer 属性使下载后续文档元素的过程将和 script.js 的下载并行进行(异步),但是 script.js 的执行要在所有元素(DOM)解析完成之后,DOMContentLoaded 事件触发之前完成。
    但async属性是HTML5新增的,使用时需注意兼容性问题。

    此处盗了个图:


    image

    该图中,绿线为HTML解析,蓝线为脚本下载,红线为脚本执行。
    可以看出默认情况下也就是同步模式下,脚本的下载和执行会阻塞HTML解析;
    异步模式中的asyncdefer两个属性在下载时均为异步不影响HTML解析,在执行时均阻塞HTML解析,但defer 胜在它可以控制脚本执行的时间,保证页面先加载出之后再执行脚本。

    3、总述

    defer外,浏览器在下载完 JS 的内容后就会立即对其解析和执行,不管是同步加载还是异步加载。
    文章中提到的异步加载,只能做到下载时的异步,但执行的时候依然会阻塞浏览器任何操作。

    相关文章

      网友评论

        本文标题:JS的同步和异步加载

        本文链接:https://www.haomeiwen.com/subject/htnvvktx.html