2008-04-02

动态加载JS

最近在写portal的时候,遇到了portlet的开发,由于portlet的量很大,所以会产生很多js文件,于是就在想,可不可以把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就可以了
评论
xiogxiog 2008-04-18
看有多少数据量 看应付什么应用的 场景不同自然用的方法也不同 对于大多数行业还是数据库的天下 因为要解决的问题不仅仅是速度 还有安全、完整、一致等很多问题, 单单强调某一方面无任何意义, 另外还要说的是 一切还是业务需求打头 技术是第二位的
huangyh 2008-04-06
要缓存和gzip压缩吗,你看看e3.resouce, http://www.javaeye.com/topic/179814
williamy 2008-04-06
很好很动态
这让我明白了为什么回字有4种写法
建议作者对aculo的loader研究一下
那是你的反面教材
可能对你的继续创作有益
microboat 2008-04-05
可以用Ext.Ajax来异步加载js片段,比如说这段js封装了一个模块类module,那么需要调用这个模块的时候就用:
 Ext.Ajax.request({
            method:'GET',
            url: 'modules/a01.js',
            scope: this,
            success: function(response){
                var module = eval(response.responseText);
                this[a01] = new module();

            }
        });

 

ecsun 2008-04-05
同时看到楼上使用这种方式执行js
window.execScript(xhr.responseText);

我更倾向于事前预留接口来执行js
这样的话,我们的代码就会相对统一些,不知道这样是不否合适?
ecsun 2008-04-05
我的主要想法是想把未来在系统做好以后,把需要不断扩展的的js写到数据库中,然后加载的缓存中,在需要这样的功能的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);
			}
		}
	};
};
rjzou2006 2008-04-05
我为说两句,dojo加载好我不否定,动态加载JS本来就无形的增加了代码的维护性不是吗?
(对于楼主的说法要将JS URL 存入数据库. 以实现动态加载的效果,对以后JS的扩展,来提供便利)
我否定使用数据库来存取这样的东西.(不光说是性能问题).
对上面说的,不能同步顺序问题.有必要在不是使用DODJ的系统为使用它的容器功能吗?

我认为:
A.动态加载这样的JS不能异步从SERVER上取数据.
B.加载JS少用DB.
C.动态加载JS本来就无形的增加了维护难度了.
D.要实现这样的功能是否有比较好的实现方法呢(共同研究)

END.
ferreousbox 2008-04-04
对于js的动态加载,其实分成两种情况,一种是有很多js,不想在页面上写很多script头,这样我们就需要使用类似dojo的方式来加载了,另外一种就是在运行的过程中执行加载,在没有运行前这个js是不存在于本地的,只有运行到后才从服务器加载,此时就需要加回调方法,当然,这种情况比较少点,也需要看具体情况做相应的调整了。
shatuo 2008-04-03
我的JS开发经验不足,就接触的来说,dojo的加载处理很好,而且dojo的扩展性很好,可以通过继承覆盖掉,加载JS对象文件貌似浏览器的限制不多吧。
dboylx 2008-04-03
to:yeahoo

有没考虑加载多个有依赖关系的类库怎么办?
yeahoo 2008-04-03
我也为没有必要放入数据库。我写了一个JavaScript的加载函数,用Ajax加载。在刚开始的时候以为用这种方式会导致浏览不再缓存JS文件了,经测试浏览器同样会从缓存中加载,使用中不会影响运行效率。
在主流浏览器上测试通过。这个加载函数在我的项目中运行的很好。

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 + ' 错误]');
        }
    }
}
dboylx 2008-04-03
shatuo 写道
dboylx 写道
DOJO,EXT等等 加载原理都是一样的,浏览器的限制。

哪方面的限制,是指XMLttpRequest的限制,还是指容器限制(只能在浏览器中运行)?


DOM,BOM,ECMAScript,CSS,HTML... 规范的限制
南极咔咔 2008-04-03
楼主思路很不错!具体验证下会更好。
shatuo 2008-04-03
dboylx 写道
DOJO,EXT等等 加载原理都是一样的,浏览器的限制。

哪方面的限制,是指XMLttpRequest的限制,还是指容器限制(只能在浏览器中运行)?
dboylx 2008-04-03
DOJO,EXT等等 加载原理都是一样的,浏览器的限制。
shatuo 2008-04-03
dojo的加载很棒,不过需要容器的帮忙。
dboylx 2008-04-03
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;
	}
}

 
ecsun 2008-04-03
我的做法是
1,不同游览器加载JS要用不同实现,这是第一步,保障代码强壮性必要条件。


这一点
目前我也是这么做的

2,做一个注册机(维护JS加载队列),任何JS加载都通过统一接口加载并注册。

3,注册机严格控制加载顺序,注册机的主要责能。这块很重要,因为不同的游览器加载的方法不一样,但加载是可以像出来的,但抽像出来的东西只能被考虑为异步,这样就迫使控制加载顺序成为一个小问题,我的做法是定义一个当前加载的LEVEL,每加载完毕一个JS文件LEVEL加一。第一次加载页面时可以同时加载N个JS文件, 另一方面的用途,在用户交互的过程中,又可以动态加载更多的JS。以当前LEVEL为参照点可以有效控制加载顺序,这也是我想出的最简单的办法。算法很简单,用JS语言的动态性可以有效的减少递归调用降低算法时间复杂度。

4,注册机加载完所有的文件,状态置为已加载完毕,保存当前加载LEVEL,并通知监听器。


加载这一块目前我没有好的办法,希望可以看到你的代码.
dboylx 2008-04-03
动态类JS加载,我觉得应用范围不广。

相对独立的,小的,与全局作用域完全无关的才可以用这个方法。但既然您已经写出了一个类库,没有强至的约束(事实是根本无法做)时很容易产生错误。
dboylx 2008-04-03
我的做法是
1,不同游览器加载JS要用不同实现,这是第一步,保障代码强壮性必要条件。

2,做一个注册机(维护JS加载队列),任何JS加载都通过统一接口加载并注册。

3,注册机严格控制加载顺序,注册机的主要责能。这块很重要,因为不同的游览器加载的方法不一样,但加载是可以像出来的,但抽像出来的东西只能被考虑为异步,这样就迫使控制加载顺序成为一个小问题,我的做法是定义一个当前加载的LEVEL,每加载完毕一个JS文件LEVEL加一。第一次加载页面时可以同时加载N个JS文件, 另一方面的用途,在用户交互的过程中,又可以动态加载更多的JS。以当前LEVEL为参照点可以有效控制加载顺序,这也是我想出的最简单的办法。算法很简单,用JS语言的动态性可以有效的减少递归调用降低算法时间复杂度。

4,注册机加载完所有的文件,状态置为已加载完毕,保存当前加载LEVEL,并通知监听器。
发表评论

您还没有登录,请登录后发表评论