当前位置 : 主页 > 网页制作 > Dojo >

让dojo.require异步加载小部件

来源:互联网 收集:自由互联 发布时间:2021-06-15
注:本文的技术实现是基于非AMD方式的。 最近用dojo开发了个系统,大约有40个widget,之前的做法是在首页里面一开始就通过dojo.require将这40多个小部件引入了,由于是在本机测试,所以一

注:本文的技术实现是基于非AMD方式的。

最近用dojo开发了个系统,大约有40个widget,之前的做法是在首页里面一开始就通过dojo.require将这40多个小部件引入了,由于是在本机测试,所以一直没有速度问题,后来部署到外网后发现效率太慢了,最长的一次需要30多秒才能完成页面初始化。虽然我一开始引入了40多个widget,但是我并不是一上来就new这么多个小部件,一开始我只用到了三四个小部件,所以只实例化了三四个。后面那37个小部件都是在用户单击某些按钮进行分析完成时实例化的,假设如果用户没有进行分析操作,那我这37个widget就白引入了,没有用到,而且造成页面初始化时间较长,所以我开始想办法优化require的机制。

我想达到的效果是:假设有一个小部件A,我想在执行var a = new A()操作之前才执行dojo.require("A"),这样不会require那些无用的小部件的js文件,就能最大限度的优化请求。如下所示:

dojo.provide("widgets.B");
dojo.require("widgets.BaseWidget");
dojo.declare("widgets.B",[widgets.BaseWidget],{
    templateString:dojo.cache("widgets.B","templates/B.html"),
    
    postCreate:function(){
        this.inherited(arguments);
        dojo.connect(this.finish,"onclick",this,this.createA);
    },
    
    createA:function(){
        dojo.require("widgets.A");
        var a = new widgets.A();
        a.placeAt(dojo.body());
        a.startup();
    },
    
    startup:function(){
        this.inherited(arguments);
    }
});

我想在单击B小部件中的finish按钮的时候执行createA方法,该方法会首先通过dojo.require("widgets.A")引入A.js,然后执行new操作。事实上这样确实也可以运行。但是我通过Chrome Developer Tools中的Network发现,在B.js这个文件被下载到浏览器中的时候就已经执行了dojo.require("widgets.A"),奇怪了,我明明写的是在createA的函数中引入的A的啊,怎么会提前引入了呢?后来发现dojo的require机制有这么个特点:当把一个文件下载到浏览器时,dojo会自动查找该文件中是否存在dojo.require()这样的语句,只要发现有,就立马去执行该语句去请求相应的js文件,而不管该语句写在文件的头部还是写在某个函数内部。   但是这样有个问题,如果在js文件中写入了dojo.require("A"),无论这样代码出现在js文件的哪个位置,dojo都会自己去请求A.js这个文件,而不是等到执行

createA:function(){
        eval("(dojo.re"+"quire('widgets.A'))");
        var a = new widgets.A();
        a.placeAt(dojo.body());
        a.startup();
    }

上面我把dojo.require拆分成了dojo.re+quire,这样dojo自身在B.js文件中找不到完整的dojo.require字符串了,也就不会一开始就去请求A.js文件了,后来通过Chrome下的Network检查发现确实是在执行createA的时候才去执行的dojo.require("widgets.A"),哈哈。但是我发现还是存在问题:dojo.require()是异步方法,不是阻塞式的,所以语句eval("(dojo.re"+"quire('widgets.A'))")执行完后不会停滞,而会立即执行下面的var a = new widgets.A()操作,但是此时A.js还没有下载到浏览器中,造成了windgets.A不存在,导致报错,这可咋整呢?

为了彻底解决这个问题,我又重新想了下,思路是:还是通过eval的方式动态的去执行dojo.require请求,用setInterval每隔一段时间判断我请求的A.js文件是否已经下载到浏览器中,如果下载完成了,那么才执行new widgets.A()操作。

全局函数如下:

//检查小部件是否已经加载
function checkWidgetLoaded(widgetName){
    var names = widgetName.split(".");
    var obj = window;
    for(var i=0;i<names.length;i++){
        var name = names[i];
        obj = obj[name];
        if(!obj){
            return false;
        }
    }
    var loaded = dojo.isFunction(obj);
    return loaded;
}

//按需加载小部件
function loadWidget(widgetName,callback){
    var loaded = window.checkWidgetLoaded(widgetName);
    if(loaded){
        if(dojo.isFunction(callback)){
            callback();
        }
    }
    else{
        var shelterDom = window.showLoading("Loading...");
        eval("(dojo.re"+"quire('"+widgetName+"'))");
        var sumTime = 0;
        var smallTime = 50;//每隔50毫秒就判断js文件是否已经引入到本地
        var maxTime = 15000;//超时时间15秒
        var handle = setInterval(dojo.hitch(this,function(shelterDom){
            var loaded = window.checkWidgetLoaded(widgetName);
            if(loaded){
                window.hideLoading(shelterDom);
                clearInterval(handle);
                if(dojo.isFunction(callback)){
                    callback();
                }
            }
            else{
                sumTime += smallTime;
                if(sumTime >= maxTime){
                    window.hideLoading(shelterDom);
                    clearInterval(handle);
                }
            }
        },shelterDom),smallTime);
    }
}

使用方法如下:

createA:function(){
        window.loadWidget("widgets.A",dojo.hitch(this,function(){
            var a = new widgets.A();
            a.placeAt(dojo.body());
            a.startup();
        }));
    }
至此,我们就完成了按需请求小部件js的加载机制,这样可以大大减少在系统一开始引入的小部件的数量。
网友评论