当前位置: 代码迷 >> JavaScript >> 优化EXTJS的按模块上载JS的功能
  详细解决方案

优化EXTJS的按模块上载JS的功能

热度:355   发布时间:2012-09-22 21:54:54.0
优化EXTJS的按模块下载JS的功能
最近有不少用户跟我反馈,访问Joffice页面的某些功能,需要等几秒钟才能出来。鉴于这种情况,在此分析一下原因,同时也给出一些解决方案,可以帮助Joffice 1.2,Joffice 1.3的开发用户解决这种根本的问题,可以让这种按模块下载js速度提高7-8倍,特别是有一些模块需要加载很多js的时候,其下载速度还高更多。



joffice 1.3以前的版本,按模块下载的原理如下:

在此我们以流程管理模块为例:



在App.import.js中定义该模块所依赖的js,如下:

   
FlowManagerView:[  
                __ctxPath+'/js/flow/ProTypeForm.js',  
                __ctxPath+'/js/flow/ProDefinitionForm.js',  
                __ctxPath+'/js/flow/ProDefinitionView.js',  
                __ctxPath+'/js/flow/FlowManagerView.js',  
                __ctxPath+'/js/flow/ProDefinitionDetail.js',  
                __ctxPath+'/js/flow/ProcessRunStart.js',  
                __ctxPath+'/js/flow/ProDefinitionSetting.js',  
                __ctxPath+'/js/flow/MyTaskView.js',  
                __ctxPath+'/js/flow/ProcessNextForm.js',  
                __ctxPath+'/js/flow/FormDesignWindow.js',  
                __ctxPath+'/js/flow/FormEditorWindow.js',  
                __ctxPath+'/js/flowDesign/FlowDesignerWindow.js'  
        ]
 
在此可以看出,该模块所依赖的js比较多,不过每个js都不大。

当点击左菜单的“流程管理”时,其就通过ScriptMgr来下载其所依赖的js,全部下载完成后,才创建这个流程管理的Panel,并且加到TabCenterPanel中去。



我们的调用下载的js代码如下:

function $ImportJs(viewName,callback,params) {  
    var b = jsCache[viewName];  
      
    if (b != null) {  
        var view =newView(viewName,params);  
        callback.call(this, view);  
    } else {  
        var jsArr = eval('App.importJs.' + viewName);  
        if(jsArr==undefined || jsArr.length==0){  
            try{  
                var view = newView(viewName,params);  
                callback.call(this, view);  
            }catch(e){  
            }  
            return ;  
        }  
        ScriptMgr.load({  
                    scripts : jsArr,  
                    callback : function() {  
                        jsCache[viewName]=0;  
                        var view = newView(viewName,params);  
                        callback.call(this, view);  
                    }  
        });  
    }  
}


即我们调用:
$ImportJs('FlowManagerView',function(){  
      return new FlowManagerView();  
});

当传入FlowManagerView时,告诉我们就是需要在App.Import.js中取出该依赖的js数组,然后传给ScriptMgr的load中的scripts参数,告诉他们我们要完成这些js的加载,并且完成后,创建FlowManagerView对象。



现在我们来看一下ScriptMgr的Load方法:
ScriptLoaderMgr = function() {  
    this.loader = new ScriptLoader();  
  
    this.load = function(o) {  
        if (!Ext.isArray(o.scripts)) {  
            o.scripts = [o.scripts];  
        }  
  
        o.url = o.scripts.shift();  
  
        if (o.scripts.length == 0) {  
            this.loader.load(o);  
        } else {  
            o.scope = this;  
            this.loader.load(o, function() {  
                        this.load(o);  
                    });  [size=medium][/size]
        }  
    };  
};

ScriptLoader的代码如下:
/** 
 * 用于动态加载js 
  *  sample is here 
  *   ScriptMgr.load({ 
  *   scripts: ['/js/other-prerequisite.js', '/js/other.js'], 
  *   callback: function() { 
  *     var other = new OtherObject(); 
  *     alert(other); //just loaded 
  *   } 
  * });  
  */  
ScriptLoader = function() {  
    this.timeout = 10;  
    this.scripts = [];  
    this.disableCaching = true;//false  
    this.loadMask = null;  
};  
  
ScriptLoader.prototype = {  
    showMask : function() {  
        if (!this.loadMask) {  
            this.loadMask = new Ext.LoadMask(Ext.getBody());  
            this.loadMask.show();  
        }  
    },  
  
    hideMask : function() {  
        if (this.loadMask) {  
            this.loadMask.hide();  
            this.loadMask = null;  
        }  
    },  
  
    processSuccess : function(response) {  
        this.scripts[response.argument.url] = true;  
        window.execScript ? window.execScript(response.responseText) : window  
                .eval(response.responseText);  
        //if (response.argument.options.scripts.length == 0) {  
            this.hideMask();  
        //}  
        if (typeof response.argument.callback == 'function') {  
            response.argument.callback.call(response.argument.scope);  
        }  
    },  
  
    processFailure : function(response) {  
        this.hideMask();  
        Ext.MessageBox.show({  
                    title : '应用程序出错',  
                    msg : 'Js脚本库加载出错,服务器可能停止,请联系管理员。',  
                    closable : false,  
                    icon : Ext.MessageBox.ERROR,  
                    minWidth : 200  
                });  
        setTimeout(function() {Ext.MessageBox.hide();}, 3000);  
    },  
  
    load : function(url, callback) {  
        var cfg, callerScope;  
        if (typeof url == 'object') { // must be config object  
            cfg = url;  
            url = cfg.url;  
            callback = callback || cfg.callback;  
            callerScope = cfg.scope;  
            if (typeof cfg.timeout != 'undefined') {  
                this.timeout = cfg.timeout;  
            }  
            if (typeof cfg.disableCaching != 'undefined') {  
                this.disableCaching = cfg.disableCaching;  
            }  
        }  
  
        if (this.scripts[url]) {  
            if (typeof callback == 'function') {  
                callback.call(callerScope || window);  
            }  
            return null;  
        }  
  
        this.showMask();  
        //alert('load now?');  
        Ext.Ajax.request({  
                    url : url,  
                    success : this.processSuccess,  
                    failure : this.processFailure,  
                    scope : this,  
                    timeout : (this.timeout * 1000),  
                    disableCaching : this.disableCaching,  
                    argument : {  
                        'url' : url,  
                        'scope' : callerScope || window,  
                        'callback' : callback,  
                        'options' : cfg  
                    }  
                });  
    }  
};

从以上我们可以看出,其加载的js数组的时候,是加载完成一个js后,然后再加载另一个js,直到加载完成后才调用回调函数。若一个模块有20个js,每个js平均下载的时间需要0.5秒,即就需要10秒钟加载。当然我们可以把这些js合并至一个js,然后下载,这是理论上是可以的,不过不利于代码的划分。



我们知道浏览器是可以同时下载这些js的,只不过我们需要知道什么时候下载完成,下载完成后我们就可以调用回调函数。



鉴于此, 我们通过一个变量来记录其下载js,每完成下载一个就自动加1,若下载完成后,下载的数量就跟我们依赖的js的数量一样,就可以回调,这样我们有多少个js,就产生多少个下载器,每个下载器同时下载这些js,改进后的ScriptMgr的代码如下所示:

ScriptLoaderMgr = function() {  
    this.loader = new ScriptLoader();  
    this.load = function(o) {  
        if (!Ext.isArray(o.scripts)) {  
            o.scripts = [o.scripts];  
        }  
        //记数器  
        o.lfiles=0;  
        for(var i=0;i<o.scripts.length;i++){  
            o.url = o.scripts[i];  
            o.scope = this;  
            this.loader.load(o, function() {  
                o.lfiles++;  
                if(o.lfiles==o.scripts.length){  
                    if(o.callback!=null){  
                        this.loader.hideMask();  
                        o.callback.call(this);  
                    }  
                }  
            });  
        }  
    };  
};


大家可以在访问体验一下:

  http://oa.jee-soft.cn:8080/index.jsp

  http://office.jee-soft.cn:8080/index.jsp

  user:csx

  pwd 111
  相关解决方案