js文件同步加载的缺点:
页面的js文件一般是同步加载,加载到js文件会阻断html和css的加载,要等到js文件加载完毕,才能继续向下执行,因为js文件可能会操作html和css;但有些js文件不会操作html和css,只是进行初始化数据或者引入工具包,我们希望这些js文件能够并行异步加载,以免一个js文件加载失败,导致后续所有页面都加载不了,影响页面效率。
另外有些工具方法需要按需加载(需要再加载,不用不加载)
js异步加载的三种方案:
1.defer 只有IE9以下能用
异步加载,但要等到dom文档全部解析完(dom树已生成)才会被执行,执行时不会阻塞页面
引入外部js文件
<script type = 'text/javascript' src = 'tools.js' defer = 'defer'> </script>
或
<script type = 'text/javascript' src = 'tools.js' defer > </script>
也可以将代码写到内部
<script type = 'text/javascript' defer>
var a = 123; //写在内部
</script>
2.async W3C标准方法
异步加载,加载完就执行,执行时不会阻塞页面,async只能加载外部js,不能把js写在script标签里
<script type = 'text/javascript' src = 'tool.js' async = 'async'></script>
或
<script type = 'text/javascript' src = 'tool.js' async ></script>
以上两种方法不能很好的处理浏览器兼容问题,因此可以使用第三种方法,更加强大,既可异步加载,也可按需加载
3.创建script节点,插入到DOM中,加载完毕后callBack
var script = document.createElement('script') //创建
script.type = 'text/javascript' //设置
script.src = 'index.js' //这句执行完 系统就会异步下载指定的文件
...//其他的操作
document.head.appendChild(script) //script标签插入文档后 系统才会解析这个脚本 否则只是下载
但由于系统是异步下载js文件,很可能script标签插入文档的时候,甚至调用js文件中方法的时候,js文件还没有下载完成
var script = document.createElement('script')
script.type = 'text/javascript'
script.src = 'index.js' //这句执行完 系统就会异步下载指定的文件
document.head.appendChild(script) //script插入文档后 才会解析这个脚本 否则只是下载
test() //系统执行这段语句的时候 js文件可能还未加载完 故会报错
可以设置定时器,在规定时间后(给js文件下载时间)再执行函数
var script = document.createElement('script')
script.type = 'text/javascript'
script.src = 'index.js' //这句执行完 系统就会异步下载指定的文件
document.head.appendChild(script) //script插入文档后 才会解析这个脚本 否则只是下载
// test()
setTimeout(function(){ //设置定时器 在规定时间后再执行函数
test() //这个时候js文件已经下载完成 可以执行
},1000)
但我们并不清楚要等待多久js文件才会下载完成,那么有没有一个机制能够提醒我们呢?
可使用load事件(并不是只有window才有load事件,但凡需要下载的就有load事件,比如script
)
var script = document.createElement('script')
script.type = 'text/javascript'
script.src = 'index.js' //这句执行完 系统就会异步下载指定的文件
script.onload = function(){ //script加载(即js文件下载完)完毕才执行函数
test()
}
document.head.appendChild(script) //script插入文档后 才会解析这个脚本 否则只是下载
script.onload
的能够兼容safari、chrome、firefox、opera,只有IE不兼容,IE没有load事件
但IE有一套自己的方法,IE的script
上有一个属性为状态码readyState
,默认值是loading
,会根据script
的加载进度动态地改变属性值,script加载完毕时状态码的值为complete
或loaded
IE也提供了一套监听机制用于监听script
的状态码的变化
script.onreadystatechange = function(){ //监听script的状态码的变化
if(script.readyState == 'complete' || 'loaded'){ //script加载完毕
// 逻辑
}
}
能够解决浏览器兼容问题的js文件加载完成立即执行函数逻辑的工具方法
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)