优化白屏!!!JS异步加载的几种方法

255 阅读3分钟

应用场景:一个70M的 geojson 格式的 js文件 用高德地图把区域渲染出来,加个点击区域弹框展示数据的功能,于是。。。

思路:70M的文件按正常的js同步运行会加载好一会儿,数据都在这个文件里,其他的操作依赖于此文件,考虑将这个70M的js拆分为十几个大小2-5M的js,异步加载依次渲染,不至于让用户等待过久

方案1:script标签,添加async属性,由于这个属性只能加载外部脚本,不能把js代码写在script标签里
async 满足异步 加载结束后会立即执行当前文件 由于项目中其他js文件依赖于 存放 geojson 的js文件,而都设置为异步加载时,小的文件先加载结束这时由于找不到依赖的数据会报错 不满足需求OUT

方案2:script标签 添加defer属性,和html同时加载,html渲染完成后执行 defer满足项目需求会按顺序执行文件,一大缺点 兼容性很差 只有IE满足 OUT

方案3:动态创建script标签

function loadScript(url,callback){ //callback回调函数可以是函数、字符串、数组等
	var script = document.createElement('script')
	script.type = 'text/javascript'
	if(script.readyState){//IE
		script.onreadystatechange = function(){ //监听script的状态码的变化
		if(script.readyState == 'complete' || 'loaded'){
			callback()
			//eval(callback) 当callback为字符串(情况1)时,eval可将其当作函数来调用
			//obj[callback]() 当callback为字符串(情况2)且所要调用的函数在js文件中是以对象属性的形式存在时
		}
	  }
	}else{ //safari、chrome、firefox、opera
		script.onload = function(){
			callback()
			//eval(callback)
			//obj[callback]()
		}
	}
	script.src = url //js文件的下载最好放在绑定监听事件之后,以免状态码在绑定监听事件之前已完成变化就不会被监听到
	document.head.appendChild(script)
}
// loadScript('index.js',test) 
//会报错 test is not defined 因为在传入test的时候,js文件还在函数中未被加载
loadScript('index.js',function(){ //可传入匿名函数 传入时函数内部逻辑不会被解析
	test()
}) 
或
loadScript('index.js','test()') //传入字符串形式(情况1)loadScript('index.js','test') //传入字符串形式(情况2)

十几个文件按此方法依次渲染,岂不是要 回调地狱OUT

方案4:最优解来了 LAB.js 只有6kb, 简洁 灵活

几个简单的案例:

**实例1:**
$LAB
    .script("script1.js")
    .script("script2.js")
    .script("script3.js")
    .wait(function(){ // 等待所有script加载完再执行这个代码块
        script1Func();
        script2Func();
        script3Func();
    });
    
**实例2:**   
$LAB    
    .script({ src: "script1.js", type: "text/javascript" })
    .script("script2.js")
    .script("script3.js")
    .wait(function(){ // 等待所有script加载完再执行这个代码块
        script1Func();
        script2Func();
        script3Func();
    });

**实例3:**
$LAB
    .script("script1.js", "script2.js", "script3.js")
    .wait(function(){ // 等待所有script加载完再执行这个代码块
        script1Func();
        script2Func();
        script3Func();
    });

**实例4:**
$LAB
    .script( [ "script1.js", "script2.js" ], "script3.js")
    .wait(function(){ // 等待所有script加载完再执行这个代码块
        script1Func();
        script2Func();
        script3Func();
    });
    
**实例5:**
$LAB
    .script("script1.js").wait() // 空的wait()只是确保script1在其他代码之前被执行
    .script("script2.js") // script2 和 script3 依赖于 script1
    .script("script3.js").wait() // 但是script2 和 script3 并不互相依赖,可以并行下载
    .script("script4.js") //  script4 依赖于 script1, script2 及 script3 
    .wait(function(){script4Func();});
    
**实例6:**
$LAB
    .script("script1.js") // script1, script2, and script3 之间没有依赖关系, 
    .script("script2.js") // 所以可以任意顺序执行
    .script("script3.js")
    .wait(function(){  // 如果需要,这里当然可以执行javascript函数
        alert("Scripts 1-3 are loaded!");
    })
    .script("script4.js") // 依赖于 script1, script2 及 script3 
    .wait(function(){script4Func();});

**实例7:**
$LAB
    .setOptions({AlwaysPreserveOrder:true}) // 设置每个脚本之间等待
    .script("script1.js") // script1, script2, script3, script4 互相依赖
    .script("script2.js") // 并且并行下载后循序执行
    .script("script3.js") 
    .script("script4.js")
    .wait(function(){script4Func();});

**实例8:**
$LAB
    .script(function(){
        // `_is_IE`的值ie为true ,非ie为false
        if (_is_IE) {
            return "ie.js"; // 如果是ie则这个js会被加载
        }
        else {
            return null; //如果不是ie这个代码就会被略过
        }
    })
    .script("script1.js")
    .wait();