【动态】简单的JS动态加载单体

动态加载外部JS关于执行顺序,其实还存在问题! //2011-07-28

-----------------------------------------------------

原文来之:博客园idche

前几天公司的项目需要这个功能,本来想找一个别人写的(别人的应该经过了许多测试,没啥问题)。

由于对这个功能的要求不高,只需要简单动态加在加载,不重复加载,简单的依赖就可以了。

最终还是自己花时间写了。

如果你熟悉YUI,

那么它跟YUI的工作方式很像

添加一个JS

WW.add('jQuery', 'http://code.jquery.com/jquery-1.6.1.min.js');

使用一个JS

 

WW.use('jQuery', function(){
document.getElementById(
'log').innerHTML += '<div>jQuery ok</div>';
});


还有关于script标签的加载完成事件

/*
var script = document.createElement('script');
IE6 IE7 IE8 IE9 Chrome Firefox Opera
script.onreadystatechange null null -- null undefined undefined undefined
script.onload undefined undefined -- null null undefined null
*/

一般返回null 说明是当前浏览器支持的事件(firefox除外,firfox其实支持的是 onload)。

最重大的发现

当你直接在页面写SCRIPT标签,与动态创建的SCRIPT标签,Opera 浏览器的事件支持是不一样的。

直接创建:事件仅仅只有 onload

动态创建:加在完成事件有 onreadystatechange  onload

最终采用了 司徒正美 的方案(感谢,最后我觉得使用“document.dispatchEvent”还是有一定道理)。

support:document.dispatchEvent ? 'onload' : 'onreadystatechange',

源码(大约100行):

View Code
/*动态JS加载*/
/*
var script = document.createElement('script');
IE6 IE7 IE8 IE9 Chrome Firefox Opera
script.onreadystatechange null null -- null undefined undefined undefined
script.onload undefined undefined -- null null undefined null
*/
/*
全局命名空间WW
主要方法
WW = {
add:fucntion(namespace, url){}
use:fucntion(namespaces, callback){}
}

方法 add(namespace, url, isOkay)
说明 注册一个JS模块
参数 namespace String
参数 url String
参数 isOkay Boolean 是否已经加载成功 可选 这个主要用于测试环境下很多文件,上线以合并成一个文件后注册空间不够

方法 use(namespaces, callback);
参数 namespaces String 可以是多个 namespace 使用逗号分割
参数 callback Function JS全部加载完成回调方法

*/
var WW = {
support:document.dispatchEvent
? 'onload' : 'onreadystatechange',
query:{
/*队列*/},
module:{
/*成功注册的模块*/},
add:
function(namespace, url, isOkay){//注册一个命名空间
var module = this.module;

if(module[namespace]){
throw namespace+ " is being";
}

module[namespace]
= {};
module[namespace].url
= url;
module[namespace].isOkay
= isOkay || false;
},
use:
function(namespaces, callback){//使用一个命名空间

var task = {};
var key;

task.namespaces
= namespaces.split(',');
task.callback
= callback || function(){};
task.complet
= 0;//已完成
task.total = task.namespaces.length;//总量
key = task.namespaces.join('');// 任务的KAY;

this.query[ key ] = task;
this.start(task, key );

},
start:
function(task, key){//内部 开始加载一个任务

var i = 0;
var len = task.namespaces.length;
var model;
var modelKey;

for(i; i<len; i++){
modelKey
= task.namespaces[i].replace(/^\s+|\s+$/g,'');
model
= this.module[ modelKey ];

if(model === undefined){
throw modelKey+' is undefined';
}

if(model.isOkay === false){
model.isOkay
= 1;
this.load(model.url, modelKey, key);
}
else if(model.isOkay === true){
this.checkBack(modelKey, key);
}
else{
this.wait(model, modelKey, key);
}
}

},
wait:
function(model, modelKey, key){// 处理,后一个任务包含前一个正在进行中的任务,而导致的重复加载问题
var _this = this;
var a = setInterval(function(){
if(model.isOkay === true){
clearInterval(a);
_this.checkBack(modelKey, key);
}
},
500);
},
checkBack:
function(modelKey, key){//内部
this.module[modelKey].isOkay = true;
var query = this.query[key];
if(++(query.complet) === query.total){
query.callback();
//回调函数
delete this.query[key];
}
},
load:
function(url, modelKey, key){//内部 加载一个JS
var _this = this;
var script = document.createElement('script');

script[
this.support] = function(){
if ( /undefined|loaded|complete/.test(script.readyState) ){
_this.checkBack(modelKey, key);
}
};
script.setAttribute(
'type', 'text/javascript');
script.setAttribute(
'src', url);
document.getElementsByTagName(
'head')[0].appendChild(script);
}
};

测试例子

posted @ 2011-06-20 10:20  Jun.lu  阅读(3327)  评论(7编辑  收藏  举报