js异步加载:关键词:异步加载(async loading),延迟加载(lazy loading),延迟执行(lazy execution),async 属性,defer 属性一、同步加载与异步加载的形式1. 同步加载我们平时最常使用的就是这种同步加载形式:<script src="/script.js"></script>同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像)、渲染、代码执行。
js 之所以要同步执行,是因为js 中可能有输出document 内容、修改dom、重定向等行为,所以默认同步执行才是安全的。
以前的一般建议是把<script>放在页面末尾</body>之前,这样尽可能减少这种阻塞行为,而先让页面展示出来。
简单说:加载的网络timeline 是瀑布模型,而异步加载的timeline 是并发模型。
2. 常见异步加载(Script DOM Element)(function(){var s = document.createElement('script');s.type ='text/javascript';s.async =true;s.src ='/script.js';var x = document.getElementsByTagName('script')[0];x.parentNode.insertBefore(s, x);})();异步加载又叫非阻塞,浏览器在下载执行js 同时,还会继续进行后续页面的处理。
这种方法是在页面中<script>标签内,用js 创建一个script 元素并插入到document 中。
这样就做到了非阻塞的下载js 代码。
async属性是HTML5中新增的异步支持,见后文解释,加上好(不加也不影响)。
此方法被称为 Script DOM Element 法,不要求js 同源。
将js代码包裹在匿名函数中并立即执行的方式是为了保护变量名泄露到外部可见,这是很常见的方式,尤其是在js 库中被普遍使用。
例如Google Analytics 和 Google+ Badge 都使用了这种异步加载代码:(function(){var ga = document.createElement('script'); ga.type ='text/javascript'; ga.async =true;ga.src =('https:'==document.location.protocol ?'https://ssl': 'http://www')+'/ga.js';var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(ga, s);})();(function(){var po = document.createElement("script");po.type ="text/javascript"; po.async =true;po.src ="https:///js/plusone.js";var s = document.getElementsByTagName("script")[0];s.parentNode.insertBefore(po, s);})();但是,这种加载方式在加载执行完之前会阻止onload 事件的触发,而现在很多页面的代码都在onload 时还要执行额外的渲染工作等,所以还是会阻塞部分页面的初始化处理。
3. onload 时的异步加载(function(){function async_load(){var s = document.createElement('script');s.type ='text/javascript';s.async =true;s.src ='/script.js';var x = document.getElementsByTagName('script')[0];x.parentNode.insertBefore(s, x);}if(window.attachEvent)window.attachEvent('onload', async_load);elsewindow.addEventListener('load', async_load,false);})();这和前面的方式差不多,但关键是它不是立即开始异步加载js ,而是在 onload 时才开始异步加载。
这样就解决了阻塞onload 事件触发的问题。
补充:DOMContentLoaded 与 OnLoad 事件DOMContentLoaded : 页面(document)已经解析完成,页面中的dom元素已经可用。
但是页面中引用的图片、subframe可能还没有加载完。
OnLoad:页面的所有资源都加载完毕(包括图片)。
浏览器的载入进度在这时才停止。
这两个时间点将页面加载的timeline分成了三个阶段。
4.异步加载的其它方法由于Javascript的动态特性,还有很多异步加载方法:∙XHR Eval∙XHR Injection∙Script in Iframe∙Script Defer∙document.write Script Tag∙还有一种方法是用 set Timeout 延迟0秒与其它方法组合。
XHR Eval :通过ajax 获取js的内容,然后 eval 执行。
var xhrObj = getXHRObject();xhrObj.onreadystatechange =function(){if( xhrObj.readyState !=4)return;eval(xhrObj.responseText);};xhrObj.open('GET','A.js',true);xhrObj.send('');Script in Ifram e:创建并插入一个iframe元素,让其异步执行js 。
var iframe = document.createElement('iframe');document.body.appendChild(iframe);var doc = iframe.contentWindow.document;doc.open().write('<body onload="insertJS()">');doc.close();GMail Mobile:页内js 的内容被注释,所以不会执行,然后在需要的时候,获取script元素中text 内容,去掉注释后eval 执行。
<script type="text/javascript">/*var ...*/</script>详见参考资料中2010年的Velocity 大会Steve Souders 和淘宝的那两个讲义。
二、async 和defer 属性1. defer 属性<script src="file.js" defer></script>defer属性声明这个脚本中将不会有 document.write 或 dom 修改。
浏览器将会并行下载file.js 和其它有defer 属性的script,而不会阻塞页面后续处理。
defer属性在IE 4.0中就实现了,超过13年了!Firefox 从3.5 开始支持defer属性。
注:所有的defer 脚本保证是按顺序依次执行的。
2. async 属性<script src="file.js" async></script>async属性是HTML5新增的。
作用和defer类似,但是它将在下载后尽快执行,不能保证脚本会按顺序执行。
它们将在onload 事件之前完成。
Firefox 3.6、Opera 10.5、IE 9 和最新的Chrome 和Safari 都支持async 属性。
可以同时使用 async 和defer,这样IE 4之后的所有IE 都支持异步加载。
3. 详细解释<script> 标签在HTML 4.01 与HTML5 的区别:∙type 属性在HTML 4中是必须的,在HTML5中是可选的。
∙async 属性是HTML5中新增的。
个别属性(xml:space)在HTML5中不支持。
说明:1.没有async 属性,script 将立即获取(下载)并执行,然后才继续后面的处理,这期间阻塞了浏览器的后续处理。
2.如果有async 属性,那么script 将被异步下载并执行,同时浏览器继续后续的处理。
3.HTML4中就有了defer属性,它提示浏览器这个 script 不会产生任何文档元素(没有docum ent.write),因此浏览器会继续后续处理和渲染。
4.如果没有async 属性但是有defer 属性,那么script 将在页面parse之后执行。
5.如果同时设置了二者,那么 defer 属性主要是为了让不支持async 属性的老浏览器按照原来的defer 方式处理,而不是同步方式。
另参见官方说明:script async个人补充:既然HTML5 中已经支持异步加载,为什么还要使用前面推荐的那种麻烦(动态创建script 元素)的方式?答:为了兼容尚不支持async 老浏览器。
如果将来所有浏览器都支持了,那么直接在script中加上async 属性是最简单的方式。
三、延迟加载(lazy loading)前面解决了异步加载(async loading)问题,再谈谈什么是延迟加载。
延迟加载:有些js 代码并不是页面初始化的时候就立刻需要的,而稍后的某些情况才需要的。
延迟加载就是一开始并不加载这些暂时不用的js,而是在需要的时候或稍后再通过js 的控制来异步加载。
也就是将js 切分成许多模块,页面初始化时只加载需要立即执行的js ,然后其它js 的加载延迟到第一次需要用到的时候再加载。