原文作者:Nicholas C. Zakas
原文链接:
Loading JavaScript without blocking
译文:
我看了Steve Souder的博文
scripts without blocking 在这篇博文中他提到动态的创建一个script元素同时为他分配一个src的属性会促成下载这个js文件而不阻塞其他的下载或者页面的其他进程。他的博文 缺少一个如何实现这种方式的例子。所以我想我。我想大多数开发者试图使用javascript脚本库来实现这种需求(我首先想到了YUI的Get utility)但是很有必要讨论一下这个技术的本质。
无阻塞的加载脚本的基本实现方式非常直接。
var scripts=document.createElement('script');
script.type="text/javascript";
script.src="file.js";
document.body.appendChild(scripts);
看 起来就是这么的简单,你创建了一个新的dom元素,给他它分配相应的属性同时把它添加到页面中。这段代码有两个方面需要注意。javascript文件直 到script节点添加到document中才开始下载。这一点和动态的创建一个img元素,为这个元素分配一个src属性这时候加载已经开始即使该节点 没有添加到document中。第二点需要注意的是你既能在<head>中添加该script节点又能在<body>中添加。这 个无关紧要。这一切的结果就是动态的加载一个javascript文件而不阻塞页面。
当然,你可能同时也想知道什么时候 javascript文件完全被加载好和执行,同时这也是事情复杂之所在。大多数modern的浏览器(firefox ,safari,opera,chrome)对于scrpt元素有一个load的事件句柄。这是一个确定脚本是否加载好的简单方式。
//Firefox, Safari, Chrome, and Opera
var scripts=document.createElement('script');
script.type="text/javascript";
script.src="file.js";
scripts.onload=function(){
alert('Script is loaded!')
}
document.body.appendChild(scripts);
真 正的问题出现在ie中,ie使用readyState属性表明script所处于的状态readystatechange事件句柄表明属性改变的时间。在 这里readyState不和xmlHttpRequest对象中一样,这里一系列的状态值不是一个数字。在这里,readyState是五个可能是属性 值。
*“uninitialized” 原始的状态。
*"loading"下载开始
*"loaded"下载完成
*"interactive"数据处在一种不完全可用
*"complete"所有的数据都可以被使用
即 使MSDN documentation表示这些是readyState可用的值,实际上,你不可能看到所有的状态。该文档同时说明其他的元素也支持 readyState同时留给我们一个相当晦涩对于readyState期望值的描述:一个对象的state首先被初始化为unintialized,然 后是loading,当数据加载完成,状态变为loaded然后是interactive最后是complete状态。
对象所有经历的状态取决于这个对象。一个对象可以跳过某个状态(比如说,interactive)如果这个状态没有被应到到这个对象。
更 奇怪的是最后的状态不总是complete。有些时候,readState停止在loaded状态而不过渡到complete状态。同时有些时候会跳过 loaded状态。最好的解决方式是检查两个readState值同时在两个事件中移除事件处理这样可以确保你不会处理两次下载。
var scripts=document.createElement('script');
script.type="text/javascript";
script.src="file.js";
document.body.appendChild(scripts);
scripts.onreadystatechange=function(){
if(readyState=="loaded"||readyState=="complete"){
scripts.onreadystatechange=null;
alert('script is loaded!')
}
}
document.body.appendChild(scripts);
你能完美的封装这两个解决方式从而创造一个跨浏览器的函数动态加载脚本。
function loadScript(url,callback){
var script=document.createElement('script');
script.type="text/javascript";
if(script.readyState){
script.onreadystatechange=function(){
if(readyState=="loaded" || readyState=="complete"){
script.onreadystatechange=null;
callback();
}
}
}else{
script.onload=function(){
callback();
}
}
script.src=url;
document.body.appendChild(script);
使用这个函数的时候,只是传入一个脚本所在的地址然后在加载好之后调用一个回调函数。
loadScript("http://yui.yahooapis.com/2.7.0/build/yahoo/yahoo-min.js",
function(){
YAHOO.namespace("mystuff");
//more...
});
以这种方式加载脚本避免脚本阻塞页面中其他资源的下载或者阻止渲染。对于提高性能这是一种相当有用的技术。真正cool的事情是YUI3构建了一个完美的无阻塞下载方式。所有你需要做的就是下载一个大约20kb的seed文件同时说明你需要下载的其他资源文件。比如这样
YUI().use("dom", function(Y){
Y.DOM.addClass(document.body, "active");
});
实际上,YUI构建了一个合适的url以便下载dom模块,当代码可执行时自动执行回调函数。这样通过异步下载其余的脚本文件从而优化了整个页面下载的时间。
无阻塞的下载一个脚本是一个需要明白的重要技术同时在关注页面下载性能的web应用程序中应用。javascript 阻塞是整个用户体验降低,但是我们已经可以对阻塞说不了。