在浏览器解析HTML文件时,会从上到下一直解析,而在一个完整的应用中,往往会通过外部js文件申明在head中。然而在js加载的时间线上,遇到script外部js, 并且没有设置async或defer属性,浏览器加载外部js并阻塞,等待js加载完成并执行该脚本,然后继续解析加载完成并执行该脚本,接着继续解析文档。
其实很多js文件不需要在初始化时全部加载完成,可以在使用时再下载js文件,原生的javascript提供了两种方式实现js文件的异步加载方式,不过由于浏览器的兼容性问题,无法同时满足,因此接下在将介绍这两种方式,并提供兼容性方法。
1、defer 属性
derfer异步加载,但要等到dom文档全部解析完才会被执行,只有IE能用
<script type="text/javascript" src="xxx.js" defer="defer"></script>
也可以异步加载内部js代码
<script defer="defer"> console.log('test'); </script>
2、async属性
async异步加载,加载完就执行,async只能加载外部脚本,不能把js卸载script标签里面,执行时也不阻塞页面。支持chrome,safari,firefox,opera浏览器
<script type="text/javascript" src="xxx.js" async="async"></script>
3、动态创建script标签
js通过document.createElement()方法动态创建script标签,当给标签src属性赋值后,会异步加载该js文件。
<script> var script = document.createElement('script'); script.type = 'text/javascript'; script.src = 'xxx.js'; document.head.appendChild(script); </script>
这个方法可以实现兼容性的js文件异步加载,但是调用异步文件里面的方法时,如果能判断浏览器是否已经加载完成呢?
接下来我们就需要封装这个方法了,并且通过一些加载的监听方法执行一些同步方法
function loadAsyncScript(url, callback) { var script = document.createElement('script'); script.type = 'text/javascript'; if (script.readyState) { // 兼容IE浏览器 // 脚本加载完成事件 script.onreadystatechange = function () { if (script.readyState === 'complete' || script.readyState === 'loaded') { callback(); } } } else { // Chrome, Safari, FireFox, Opera可执行 // 脚本加载完成事件 script.onload = function () { callback(); } } script.src = url; //将src属性放在后面,保证监听函数能够起作用 document.head.appendChild(script); }
封装的方法loadAsyncScript(url, callback)传入两个参数,一个js文件的地址,另外一个是回掉函数,让的用户可以再里面执行异步加载文件里面的方法或者对象。
比如test.js文件里面有一个test()方法,想要异步的方式加载test.js, 并且能够执行里面的函数,调用的方式如下:
loadAsyncScript('test.js', function () { test(); })在loadAsyncScript(url, callback)方法中,可以根据自己的业务需求调整回调函数的执行方式和执行条件。