通常来说,浏览器对于Javascript
的运行有两大特性:
- 载入后马上执行
- 执行时会阻塞页面后续的内容(包括页面的渲染/其他资源的加载)。于是,如果有多个js文件被引入,那么对于浏览器来说,这些js文件被穿行地载入,并一次执行。
因为javascrit
可能会操作HTML文档的DOM树,所以,浏览器一般都不会像并行下载css
文件并行下载js
文件,因为这是js
文件的特殊性造成的。所以,如果你的javascript
像操作后面的DOM元素,基本上来说,浏览器都会报错说对象找不到。因为javascript
执行时,后面的html
被阻塞住了,DOM
树时还没有后面的DOM节点。所以程序也就报错了。
传统的方式
所以,当你在代码中写下如下代码:
<script type="text/javascript" src="http://coolshell.cn/asyncjs/alert.js"></script>
基本上来说,head
里的script
标签会阻塞后续资源的载入以及整个页面的生成。
所以,你知道为啥那么有很多网站把javascript
放在网页的最后面了,要么就是动用window.onload
或是$(document).ready(function(){})
之类的事件。
另外,因为绝大多数哦的javascrit
代码并不需要等页面,即:异步载入。那我们如何异步载入呢?
document.write
方式
于是,你可能以为document.write()
这种方式能够解决阻塞问题。你当然会觉得,document.write
了的script
标签后就可以执行后面的东西了,者没错。对于在同一个script
标签例的javascript
的代码来说,是这样的,但是对于整个页面来说,这个还是会阻塞,下面时一段测试代码:
<script type="text/javascript" language="javascript">
function loadjs(script_filename) {
document.write('<' + 'script language="javascript" type="text/javascript"');
document.write(' src="' + script_filename + '">');
document.write('<'+'/script'+'>');
alert("loadjs() exit...");
}
var script = 'http://coolshell.cn/asyncjs/alert.js';
loadjs(script);
alert("loadjs() finished!");
</script>
<script type="text/javascript" language="javascript">
alert("another block");
</script>
依此弹出的对话框为:
loadjs() exit...
loadjs() finished!
hello world
another block
然后才会显示页面。
script
的defer
和async
属性
IE自从IE6就支持defer
,如:
<script defer type="text/javascript" src="./alert.js" ></script>
对于IE来说,这个标签会让IE并行下载js文件,并且把其执行hold到了整个DOM装载完毕(DOMContentLoaded),多个defer
的script
在窒息感时也会按照其出现的顺序来运行。但是因为这个defer
只时IE专用,所以一般用的比较少。
而我们标砖的HTML5也加入了一个异步载入javascript
的属性:async
,无论你对它都什么样的值,只要它出现,它就开始异步加载js文件。但是,async
的异步加载会有一个严重的问题,那就是它忠实地碱性者“载入后马上执行”这条军规,所以,虽然它并不阻塞页面渲染,但是你也无法控制他执行的次序和时机。
支持async
标签的浏览器是:Firefox3.6+,Chrome8.0+,Safari5.0,IE10+。Opera还不支持参考这里所以这个方法也不是太好。因此不是所有的浏览器你都能行。
动态创建DOM方式
这种方式可能是用的最多的了。
function loadjs(script_name){
var script = doucment.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', script_name);
script.setAttribute('id', 'my_script_id');
script_id = document.getElementById('my_script_id');
if(script_id){
document.getElementsByTagName('head')[0].removeChild(script_id);
}
document.getElementsByTagName('head')[0].appendChild(script);
};
var script = 'http://coolshell.cn/asyncjs/alert.js';
loadjs(script);
这个方式几乎成了标准的异步载入js文件的方式。
按需异步载入js
上面的那个DOM方式的例子解决了异步载入javascript的问题,但是没有解决我们想让它按照我们指定的时机运行的问题。所以,我们只需要把上面的那个DOM方式帮到某个事件上来就可以了。
比如:
1) 绑在window.load 事件上(示例)
window.load = loadjs("http://coolshell.cn/asyncjs/alert.js")
2) 绑在特定的事件上(示例)
<p style="cursor: pointer" onclick="LoadJS()">Click to load alert.js </p>
比如当我们在点击某个DOM元素时,才载入我们的JS文件。
更多
但是,绑定在某个特定事件上这个事似乎又过了点,因此只有在点击的时候才会去真正的下载js,这又会太慢了。,我们想要异步地把嘉实稳键下载到本地,但又不执行,仅当在我们想要执行的时候去执行。
要是我们又下面的方式就好了:
var script = document.createElement("script");
script.noexecute = true;
script.src = "alert.js";
document.body.appendChild(script);
//后面我们可以这么干
script.execute();
可惜的是,这只是一个美丽的梦境,今天我们的javascript
还比较原始,这个js梦还没有实现。
所以,我们的程序员只能是哟个hack的方式来搞。
有的程序员使用非标准的script的type来实现。如:
<script type=cache/script src="./alert.js"></script>
因为“cache/script
”,这个东西根本就不能被浏览器歇息,所以浏览器也就不能把alert.js
当javascript
执行,但是它又要去下载嘉实稳键,所以就可以搞定了。可惜的是,webkit
严格服从了HTML标准–对于这个不认识的东西,直接删除,什么也不干,于是我们的梦又破了。
所以,我们需要在hack
以下,就是preload
图片那样,我们可以动用object
标签(也可以使用iframe
标签),于是我们有了下面这样的代码:
function cachejs(script_filename){
var cache = document.createElement('object');
cache.data = script_filename;
cache.id = "coolshell_script_cache_id";
cache.width = 0;
cache.height = 0;
document.body.appendChild(cache);
}
在Chrome下按Ctrl+Shift+I。切换到network页,你可以看到下载了alert.js
但是没有执行,因为浏览器缓存了,不会再从服务器上下载alert.js
,所以,就能保证执行速度了。
最后在提两个js,一个事ControlJs,另一个是HeadJs都是专门用来做异步load javascript文件的。