这几天一直在忙项目上线的问题,碰到了好多问题,比如异步加载、事件冒泡、浏览器缓存。这篇文章主要是文件的同步加载和异步加载。
先说下我碰到的问题:项目要接入到支付宝城市服务中,但是安卓和ios设置支付宝的导航栏内容有差异。安卓和ios都是拿页面的title设置导航栏的内容,但是安卓支持用js动态的修改title,ios是在页面初始化时会默认拿title作为导航栏内容,初始化后用JS动态修改无效。支付宝提供了设置支付宝导航的jsAPI,需要加载两个文件,我之前是在项目中直接加载这连个文件,在调用它提供的方法,没问题完美设置。但是后来我感觉这样不好我不是每个输出的商户都需要设置,所以我用navigator.userAgent判断是不是在支付宝客户端打开,在动态添加这两个JS文件,在这就出现问题了,你动态添加完JS后,马上执行里面的方法有时会出现错误,说明你还没有加载成功时就执行了方法。
这里就是我们经常说的同步加载与异步加载:
1. 同步加载
我们平时最常使用的就是这种同步加载形式:
<script src="js/jQuery.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 = 'js/jQuery.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
})();
jQuery方法
$("head").append(“jQuery.js”)
异步加载又叫非阻塞,浏览器在下载执行 js 同时,还会继续进行后续页面的处理。
这种方法是在页面中<script>标签内,用 js 创建一个 script 元素并插入到 document 中。这样就做到了非阻塞的下载 js 代码。
3. onload 时的异步加载
(function() {
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'js/jQuery.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent)
window.attachEvent('onload', async_load);
else
window.addEventListener('load', async_load, false);
})();
这和前面的方式差不多,但关键是它不是立即开始异步加载 js ,而是在 onload 时才开始异步加载。这样就解决了阻塞 onload 事件触发的问题。
补充:DOMContentLoaded 与 OnLoad 事件
DOMContentLoaded : 页面(document)已经解析完成,页面中的dom元素已经可用。但是页面中引用的图片、subframe可能还没有加载完。
OnLoad:页面的所有资源都加载完毕(包括图片)。浏览器的载入进度在这时才停止。
4. jQuery的异步加载
jQuery.getScript(url, [callback])
参数:url:待载入 JS 文件地址。
callback:成功载入后回调函数。
$.getScript("test.js", function(){
alert("Script loaded and executed.");
});
这和前面的方法差不多加载 test.js 成功后,调用方法显示信息。
5、async 和 defer 属性
1. defer 属性
<script src="js/jQuery.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="js/jQuery.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 不会产生任何文档元素(没有document.write),因此浏览器会继续后续处理和渲染。
4. 如果没有 async 属性 但是有 defer 属性,那么script 将在页面parse之后执行。
5. 如果同时设置了二者,那么 defer 属性主要是为了让不支持 async 属性的老浏览器按照原来的 defer 方式处理,而不是同步方式。