异步加载js


首先铺垫一下浏览器的绘制页面过程:、

  1. 第一步,首先是建立一个dom树,它是解析所有的dom节点(深度遍历的方法),解析dom节点就是解析到哪个dom节点就把它挂到dom树上,像碰到<img src = "XXX"/>这样的还是只把img节点挂到dom树上,意思就是说dom树的构建完成就是所有dom节点的解析完毕,并不代表所有dom节点加载完毕(dom的解析完毕一定在dom加载完毕之前)。
  2. 第二步生成cssTree
  3. 第三步生成randertree

因为js可以操作dom,所以当渲染树建立后,如果动态的操作了dom,机会导致页面重新渲染,叫做reflow重排(重构)
dom节点的删除,添加。dom节点的宽高变化,位置变化,displaynone–>block,offsetwidth,offsetLeft等一系列都会导致重排。

repaint重绘:js改变了节点 的css,会导致那一部分的重绘,这样影响比较小。

异步加载js

js加载的缺点

js加载本身是属于同步加载的,加载js文件会阻塞文档,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作。

但是有些工具方法需要按需加载,有一些工具js文件它是不会改变页面的,用到再加
载,不用不加载。

javascript异步加载的三种的方案
1. defer异步加载

defer异步加载,但要等到dom文档全部解析完才会被执行,只有IE能用,也可以将代码写到内部。
在这里插入图片描述在这里插入图片描述

2. async 异步加载

async 异步加载,加载完就执行,async 只能加载外部脚本,不能把js写在script标签里。
asychronous javscript and xml---->ajax的缩写

( 1和2执行时不阻塞页面,但是兼容性不好)

3. 创建script,插入到DOM中,加载完毕后callBack
<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件


       document.head.appendChild(script);//当把标签插入到页面的时候才会去执行这个js脚本

 </script>

<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件

       document.head.appendChild(script);//当把标签插入到页面的时候才会去执行这个js脚本
       test();//Uncaught ReferenceError: test is not defined
  //为什么当前执行不了,因为文件还没有下载完,读程序是以微秒计的。有可能异步的下载demo.js文件还没有下载完,程序就读到test执行这了,还没有下载完demo.js,当然没有test函数了      
  
    </script>
<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件

       document.head.appendChild(script);//当把标签插入到页面的时候才会去执行这个js脚本
       setTimeout(function () {
            test();
        }, 1000)
 //这样就可以执行test函数了。等一段时间,等demo.js文件下载完毕了,再执行。
  </script>     

但是我们怎么知道它什么时候异步的把外部js文件下载完呢???
这个时候就要有一个信号来提示我们外部js文件异步下载完毕,你可以用了。

  1. load事件监听:Safari,chrome,firefox,opera都可以用这个来监听什么时候这个文件异步下载完毕
<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";
       script.onload = function(){
             test();//当文件下载完之后就会执行onload方法,这样就知道什么时候demo.js加载完了。
       }
       document.head.appendChild(script);
    </script>
  1. IE 就用状态码的改变来知道什么时候完成文件的下载
<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";
	   script.onreadystatechange = function(){
           if(script.readyState == "complete" || script.readyState == "loaded"){
               test();
           }
       }
	   document.head.appendChild(script);
    </script>

  1. 综合1和2,实现所有浏览器兼容
<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件
       
       if(script.readyState){
            script.onreadystatechange = function(){
            if(script.readyState == "complete" || script.readyState == "loaded"){
                test();
            }
         }
       }else{
            script.onload = function(){
                test();
            }
       }
   
       document.head.appendChild(script);
      
    
    </script>
  1. 封装一个函数兼容性的异步加载js文件并且可以按需执行该文件里面的函数
    第一版:
<script>
//callback名字叫做回调函数,在事件里面的绑定的事件处理函数就是回调函数,回调函数其实就是当满足一定条件才调用的函数。
       function loadScript (url, callback){
           // url是异步下载的js文件
           //callback是异步下载的js文件中的某一个函数
        var script = document.createElement('script');
        script.type = "text/javascript";
        script.src = url;//异步下载的js文件
        if(script.readyState){
            script.onreadystatechange = function(){
                if(script.readyState == "complete" || script.readyState == "loaded"){
                    callback();
                }
           }
        }else{
            script.onload = function(){
                callback();
            }
        }
        
        document.head.appendChild(script);
       }
</script>
第一版有一个缺陷就是在IE上如果下载太快(比读程序还快),IE的readystatechange 事件检测状态码的时候,它早已经从loading变成complete或者loaded(以极快的速度加载完了文件,你还没来得及检测),
那你再检测它就不会变了,它一直都是complete或者loaded,这个时候就是马后炮了,检测也没用了。

第二版(完美版):


<script>
function loadScript (url, callback){
           // url是异步下载的js文件
           //callback是异步下载的js文件中的某一个函数
        var script = document.createElement('script');
        script.type = "text/javascript";
  
        if(script.readyState){
            script.onreadystatechange = function(){
                if(script.readyState == "complete" || script.readyState == "loaded"){
                    callback();
                }
           }
        }else{
            script.onload = function(){
                callback();
            }
        }
        script.src = url;//开始异步下载的js文件
        document.head.appendChild(script);
       }
</script>
第二版就是先绑定事件,然后再script.src = url开始异步下载js文件,那么这个时候下载,肯定有一个状态码的转换,这样就解决了第一版还来不及检测状态码的尴尬。    

封装函数的用法完善(截图中省略了loadScript,它就是上面的完整版):
在这里插入图片描述第一种解决办法:
在这里插入图片描述
第二种解决办法(当要异步加载的js文件里面的函数构造是以这种命名空间的形式):
在这里插入图片描述

<script>
function loadScript (url, callback){
     // url是异步下载的js文件
     //callback是异步下载的js文件中的某一个函数
	  var script = document.createElement('script');
	  script.type = "text/javascript";
	 
	  if(script.readyState){
	      script.onreadystatechange = function(){
	          if(script.readyState == "complete" || script.readyState == "loaded"){
	              tools[callback]();
	          }
	     }
	  }else{
	      script.onload = function(){
	          tools[callback]();
	      }
	  }
	  script.src = url;
	  document.head.appendChild(script);
 }
 
   loadScript('demo.js', "test");
   //传进去一个字符串,然后 tools[callback]();里面的tools[callback]拿到的就是test函数体,后面带上执行符号()执行就可以了。
   </script>
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值