2008-04-02
动态加载JS
最近在写portal的时候,遇到了portlet的开发,由于portlet的量很大,所以会产生很多js文件,于是就在想,可不可以把js写到数据库中,然后在运行的时候,按需动态加载,做了一下尝试,发现是可以的.
先给出代码:
从数据库里面读js内容,很简单了,只要将jsDom.text=xRequest.responseText;
换成从数据库中取得的js就可以了
先给出代码:
/**
* www.faceye.com 网络支持系统
* 作者:宋海鹏 ecsun@sohu.com/myecsun@hotmail.com/QQ:82676683
* 说明:javascripts 工具类
*
*/
/**
* 动态加载JS文件
*/
var Faceye = {
version : 1.0,
/**
*简单取得httpRequest
*/
httpRequest : function() {
var xRequest = null;
if (window.XMLHttpRequest) {
xRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xRequest = new ActiveXObject("MsXml2.XmlHttp");
}
return xRequest;
},
/**
*简单的加载文件
*/
load : function(src) {
var headerDom = document.getElementsByTagName('head').item(0);
var jsDom = document.createElement('script');
jsDom.type = 'text/javascript';
jsDom.scr = src;
headerDom.appendChild(jsDom);
},
/**
*通过ajax方式加载js
*/
ajaxLoad : function(src) {
var xRequest = this.httpRequest();
xRequest.open('GET',src,true);
xRequest.send(null);
var headerDom = document.getElementsByTagName('head')
.item(0);
var jsDom = document.createElement('script');
jsDom.type = 'text/javascript';
jsDom.language='javascript';
jsDom.defer=true;
jsDom.text=xRequest.responseText;
headerDom.appendChild(jsDom);
}
};
从数据库里面读js内容,很简单了,只要将jsDom.text=xRequest.responseText;
换成从数据库中取得的js就可以了


评论
这让我明白了为什么回字有4种写法
建议作者对aculo的loader研究一下
那是你的反面教材
可能对你的继续创作有益
Ext.Ajax.request({ method:'GET', url: 'modules/a01.js', scope: this, success: function(response){ var module = eval(response.responseText); this[a01] = new module(); } });window.execScript(xhr.responseText);
我更倾向于事前预留接口来执行js
这样的话,我们的代码就会相对统一些,不知道这样是不否合适?
整体结构就像,我们在框架中留下调用的接口,然后,我们对接口的具体实现,是在数据库中做的,这样一来,我们可以编写一些标准的组件,比如说window,form,panel等,而当我们需要填充panel的时候,我们就通过预先预留出来的接口,从缓存中加载.
这样的思路最初来源于在项目中对模板的使用,在我们使用velocity或是freemarker的时候,我可以在模板中调用相应的类,当我们的项目部署在线上运行的时候,这个时候,频繁的改动java代码,就需要频繁的去重启服务器,对于web 应用来说,频繁的重启服务器,是相当致命的, 所以,对于核心的功能,我们放在java代码中执行,对于相对简单的功能,我们就放在模板里面执行,这样的话,即使需要改动,也不至于频繁的去重启服务器.
这一点,也是我把js代码放入数据库的一个的初衷.经过摸索,发现这个方法,确实可行,把基本的代码贴出来,大伙讨论一下,当然,dojo的加载方式,我还没有具体去看,只是看到dojo导入包的时候,使用importot.
以下是基础的从数据库动态加载js的代码.
/** * 动态加载类 */ var Faceye = { version : '1.0', /** * 取得xmlHttpRequest */ httpRequest : function() { var xRequest = null; if (window.XMLHttpRequest) { xRequest = new XMLHttpRequest(); } else if (window.ActiveXObject) { xRequest = new ActiveXObject("MsXml2.XmlHttp"); } return xRequest; }, open : function(url, params, httpMethod) { var xRequest = this.httpRequest(); xRequest.open(httpMethod, url, false); xRequest.send(); return xRequest; } }; var ClassLoader = function() { }; /** * 根据文件路径进行加载 */ ClassLoader.prototype.loadFormFileSrc = function(url) { var xRequest = Faceye.httpRequest(); xRequest.onreadystatechange = function() { var state = xRequest.readyState; if (state == 4) { if (xRequest.status == 200 || xRequest.status == 304) { var source = xRequest.responseText; var headerDom = document.getElementsByTagName('head').item(0); var jsDom = document.createElement('script'); jsDom.type = 'text/javascript'; jsDom.language = 'javascript'; jsDom.defer = true; jsDom.text = source; headerDom.appendChild(jsDom); } } }; xRequest.open('GET', url, false); xRequest.send(null); }; /** * 加载javascript源文件代码 */ ClassLoader.prototype.loadFromSource = function(source) { var xRequest = Faceye.httpRequest(); xRequest.onreadystatechange = function() { var state = xRequest.readyState; if (state == 4) { if (xRequest.status == 200 || xRequest.status == 304) { var headerDom = document.getElementsByTagName('head').item(0); var jsDom = document.createElement('script'); jsDom.type = 'text/javascript'; jsDom.language = 'javascript'; jsDom.defer = true; jsDom.text = source; headerDom.appendChild(jsDom); } } }; };(对于楼主的说法要将JS URL 存入数据库. 以实现动态加载的效果,对以后JS的扩展,来提供便利)
我否定使用数据库来存取这样的东西.(不光说是性能问题).
对上面说的,不能同步顺序问题.有必要在不是使用DODJ的系统为使用它的容器功能吗?
我认为:
A.动态加载这样的JS不能异步从SERVER上取数据.
B.加载JS少用DB.
C.动态加载JS本来就无形的增加了维护难度了.
D.要实现这样的功能是否有比较好的实现方法呢(共同研究)
END.
有没考虑加载多个有依赖关系的类库怎么办?
在主流浏览器上测试通过。这个加载函数在我的项目中运行的很好。
var $_JS_LOADED = []; //已载入的JavaScript文件 //动态加载JavaScript文件 function $import(src){ if(!$_JS_LOADED[src]){ try{ var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); xhr.open("GET", src, false); xhr.send(null); if(200 == xhr.status || 0 == xhr.status){ if (window.execScript) window.execScript(xhr.responseText); else window.eval.call(window, xhr.responseText); } $_JS_LOADED[src] = true; }catch(e){ alert('系统错误: 加载文件"' + src + '"出错! \r\n[在第 ' + e.lineNumber + ' 行出现 ' + e.message + ' 错误]'); } } }哪方面的限制,是指XMLttpRequest的限制,还是指容器限制(只能在浏览器中运行)?
DOM,BOM,ECMAScript,CSS,HTML... 规范的限制
哪方面的限制,是指XMLttpRequest的限制,还是指容器限制(只能在浏览器中运行)?
JSLoader=function (){ var cbArr = new Array(); function execCBs(){ for(var i=0;i<cbArr.length;i++){ if(cbArr[i].cb){ try{ if(typeof(cbArr[i].cb) == "function"){ if(typeof(cbArr[i].scope)!="undefined") cbArr[i].cb.call(cbArr[i].scope) else cbArr[i].cb(); } } catch(e){alert('script error!')}; cbArr.splice(i--,1); } } }; return { //加载后的回调 onReady : function(callBack,d){ cbArr.push({cb : callBack,scope : d}); }, //回调方法的运行 execFcs : function(){ execCBs(); }, Ajax : { //AJAX实现 } }; }(); JSLoader.jsCache = {currentLevel:1,levelCounter:1,ready:false}; JSLoader.require=function(fileName){ var inStack = false; if(JSLoader.jsCache[fileName]){ return; }else{ var js = new Js(fileName); JSLoader.jsCache[fileName] = js; JSLoader.jsCache.ready=false; var currentLevel = JSLoader.jsCache.levelCounter++; JSLoader.Ajax.request('GET',fileName,{ success:function(response){ JSLoader.jsCache[fileName].loaded = true; JSLoader.jsCache[fileName].content = response.responseText; JSLoader.jsCache[currentLevel] = JSLoader.jsCache[fileName] executeScript(currentLevel); }, scope:this } ); } function executeScript(executeLevel){ if(executeLevel == JSLoader.jsCache.currentLevel && JSLoader.jsCache[executeLevel].loaded){ executeJs(JSLoader.jsCache[executeLevel]) if(executeLevel == (JSLoader.jsCache.levelCounter - 1)) JSLoader.execFcs(); if(++JSLoader.jsCache.currentLevel < JSLoader.jsCache.levelCounter && JSLoader.jsCache[JSLoader.jsCache.currentLevel] && JSLoader.jsCache[JSLoader.jsCache.currentLevel].loaded){ executeScript(JSLoader.jsCache.currentLevel); } } }; function executeJs(jsObj){ if(window.executeScript){ window.executeScript(jsObj.content); }else if(window.execScript){ window.execScript(jsObj.content) }else{ window.eval(jsObj.content); } jsObj.content = null; jsObj.excuted = true; jsObj.content = false; } function Js(fn){ this.fileName=fn; this.loaded=false; this.content=null; this.excuted=false; } }1,不同游览器加载JS要用不同实现,这是第一步,保障代码强壮性必要条件。
这一点
目前我也是这么做的
2,做一个注册机(维护JS加载队列),任何JS加载都通过统一接口加载并注册。
3,注册机严格控制加载顺序,注册机的主要责能。这块很重要,因为不同的游览器加载的方法不一样,但加载是可以像出来的,但抽像出来的东西只能被考虑为异步,这样就迫使控制加载顺序成为一个小问题,我的做法是定义一个当前加载的LEVEL,每加载完毕一个JS文件LEVEL加一。第一次加载页面时可以同时加载N个JS文件, 另一方面的用途,在用户交互的过程中,又可以动态加载更多的JS。以当前LEVEL为参照点可以有效控制加载顺序,这也是我想出的最简单的办法。算法很简单,用JS语言的动态性可以有效的减少递归调用降低算法时间复杂度。
4,注册机加载完所有的文件,状态置为已加载完毕,保存当前加载LEVEL,并通知监听器。
加载这一块目前我没有好的办法,希望可以看到你的代码.
相对独立的,小的,与全局作用域完全无关的才可以用这个方法。但既然您已经写出了一个类库,没有强至的约束(事实是根本无法做)时很容易产生错误。
1,不同游览器加载JS要用不同实现,这是第一步,保障代码强壮性必要条件。
2,做一个注册机(维护JS加载队列),任何JS加载都通过统一接口加载并注册。
3,注册机严格控制加载顺序,注册机的主要责能。这块很重要,因为不同的游览器加载的方法不一样,但加载是可以像出来的,但抽像出来的东西只能被考虑为异步,这样就迫使控制加载顺序成为一个小问题,我的做法是定义一个当前加载的LEVEL,每加载完毕一个JS文件LEVEL加一。第一次加载页面时可以同时加载N个JS文件, 另一方面的用途,在用户交互的过程中,又可以动态加载更多的JS。以当前LEVEL为参照点可以有效控制加载顺序,这也是我想出的最简单的办法。算法很简单,用JS语言的动态性可以有效的减少递归调用降低算法时间复杂度。
4,注册机加载完所有的文件,状态置为已加载完毕,保存当前加载LEVEL,并通知监听器。