dojo/request模块整体架构解析
发布日期:2021-05-09 02:06:43 浏览次数:8 分类:博客文章

本文共 9908 字,大约阅读时间需要 33 分钟。

  总体说明

  做前端当然少不了ajax的使用,使用dojo的童鞋都知道dojo是基于模块化管理的前端框架,其中对ajax的处理位于dojo/request模块。一般情况下我们使用ajax请求只需要引入dojo/request模块,然后按照文档的说明制定参数即可。实际上dojo在这一模块的处理中抽象了很多概念:

  • 平台侦探器:dojo/request/default
  • 请求分发器:dojo/request/registry
  • 全局通知器:dojo/request/notify
  • 数据传输器:dojo/request/xhr dojo/request/script dojo/request/iframe dojo/request/node
  • 数据转化器:dojo/request/handlers

  处理器的总体关系如下图所示:

  

  正是这些概念使得dojo在ajax请求上能够提供强大的扩展性和简捷的接口。

 

  Provider

  请求传输器被称为Provider,dojo框架自身提供了以下4个provider

  • dojo/request/xhr 提供跨浏览器的XMLHttpRequest,在浏览器端它被作为默认的provider
  • dojo/request/node 用于node平台的异步请求,在node下呗当做默认的provider。dojo是可以运行在node平台下的,当然需要做一些配置,这是另一篇文章的主要内容
  • dojo/request/iframe 不刷新浏览器传输form表单,在文件上传时经常用到
  • dojo/request/script 常以jsonp方式来进行跨域请求

  所有dojo自带的Provider返回一个,其中有一个不在标准规范内的属性:response。该属性是一个标准promise对象,该对象将一个代表服务器端响应结果的对象作为fulfill状态的值。这个对象有以下几个属性:

  关于这几个Provider的详细讲解请继续关注下一篇文章

  

  default

  一般情况下我们发送ajax请求时只需引入dojo/request即可,实际上这是在default中根据不同的运行平台自动给我们提供了一个默认的provider。

1 define([ 2     'exports', 3     'require', 4     '../has' 5 ], function(exports, require, has){ 6     //读取dojoConfig中的配置信息 7     var defId = has('config-requestProvider'), 8         platformId; 9     10     //根据不同平台选择不同的provider11     if(has('host-browser') || has('host-webworker')){12         platformId = './xhr';13     }else if(has('host-node')){14         platformId = './node';15     /* TODO:16     }else if(has('host-rhino')){17         platformId = './rhino';18    */19     }20 21     if(!defId){22         defId = platformId;23     }24 25     exports.getPlatformDefaultId = function(){26         return platformId;27     };28     //作为插件使用,是跟参数选择provider29     exports.load = function(id, parentRequire, loaded, config){30         require([id == 'platform' ? platformId : defId], function(provider){31             loaded(provider);32         });33     };34 });
View Code

  代码中关于exports跟require模块的说明请看我的上一篇博客:。

  上述内容关于load的函数的出现,意味着该模块可以作为“”使用。dojo插件主要用于加载一些非AMD的资源,比如css、html。dojo中常用的插件有5个:

  • dojo/domReady 
  • dojo/text 用于加载静态资源文件
  • dojo/i18n 加载国际化语言文件
  • dojo/has 用于特性检测
  • dojo/require

  当在define或require中一个模块引用包含一个!,dojo的加载器会自动将这个模块引用字符串在!处分开,左边部分作为一个模块引用对待,右边部分,等待左边模块加载完毕后交由模块的load方法处理;

exports.load = function(id, parentRequire, loaded, config){        require([id == 'platform' ? platformId : defId], function(provider){            loaded(provider);        });    };

  关于load函数的几个参数:

  • id:代表!右侧部分
  • parentRequire:上下文智能的require请求器
  • loaded:id模块加载完毕后的回调
  • config:猜测是dojo/_base/config

  后三个参数是dojo自己来处理,一般情况下我们不需要关心。

  关于插件还要在说几句:
  dojo中不会像缓存module一样缓存插件所加载的资源比如:我们可以多次引用同一个module,但是这个module只会加载一次,这是AMD规范所强制规定的。但是我如果多次dojo/text!./template.html这个template.html会被加载多次。

  

  notify

  是全局的ajax事件通知器,负责全局范围内的ajax事件监听,有类似于jquery中ajaxStart、ajaxComplete的事件。

1 define(['../Evented', '../_base/lang', './util'], function(Evented, lang, util){ 2     // module: 3     //        dojo/request/notify 4     // summary: 5     //        Global notification API for dojo/request. Notifications will 6     //        only be emitted if this module is required. 7     // 8     //        | require('dojo/request', 'dojo/request/notify', 9     //        |     function(request, notify){10     //        |         notify('load', function(response){11     //        |             if(response.url === 'someUrl.html'){12     //        |                 console.log('Loaded!');13     //        |             }14     //        |         });15     //        |         request.get('someUrl.html');16     //        |     }17     //        | );18 19     var pubCount = 0,20         slice = [].slice;21     //实例化dojo/Evented对象,负责分发事件22     var hub = lang.mixin(new Evented, {23         onsend: function(data){24             if(!pubCount){25                 this.emit('start');26             }27             pubCount++;28         },29         _onload: function(data){30             this.emit('done', data);31         },32         _onerror: function(data){33             this.emit('done', data);34         },35         _ondone: function(data){36             if(--pubCount <= 0){37                 pubCount = 0;38                 this.emit('stop');39             }40         },41         emit: function(type, event){42             var result = Evented.prototype.emit.apply(this, arguments);43 44             // After all event handlers have run, run _on* handler45             //运行完标准事件处理函数后,再来运行本身的私有函数。46             //load和error事件处理完后触发done事件47             //done事件处理完毕后,再来运行本身的_ondone函数,然后触发stop事件48             if(this['_on' + type]){49                 this['_on' + type].apply(this, slice.call(arguments, 1));50             }51             return result;52         }53     });54 55     function notify(type, listener){56         // summary:57         //        Register a listener to be notified when an event58         //        in dojo/request happens.59         // type: String?60         //        The event to listen for. Events emitted: "start", "send",61         //        "load", "error", "done", "stop".62         // listener: Function?63         //        A callback to be run when an event happens.64         // returns:65         //        A signal object that can be used to cancel the listener.66         //        If remove() is called on this signal object, it will67         //        stop the listener from being executed.68         return hub.on(type, listener);69     }70     notify.emit = function(type, event, cancel){71         return hub.emit(type, event, cancel);72     };73 74     // Attach notify to dojo/request/util to avoid75     // try{ require('./notify'); }catch(e){}76     return util.notify = notify;77 });
View Code

  最后的一句:util.notify= notify; util将notify与provider关联起来。

 

  registry

  该模块可以在不同的情况下使用不同的provider;匹配的条件可以是正则表达式、字符串或者函数。通过registry可以根据不同的条件注册不同的provider。

1 require(["dojo/request/registry", "dojo/Deferred"], function(request, Deferred){ 2   request.register("crossdomain/ie", xdrProvider); 3  4   var xdrProvider = function(url, options){ 5     var def = new Deferred(); 6     xdr = new XDomainRequest(); 7     if (xdr) { 8       xdr.onerror = function(){ 9         def.reject('error');10       };11       xdr.ontimeout = function(){12         def.reject('timeout');13       };14       xdr.onprogress = function(){15         def.progress('progress');16       };17       xdr.onload = function(res){18         def.resolve(res);19       };20       xdr.timeout = 6000;21       xdr.open(options.method, url);22       xdr.send(serilize(options.data));23     } else {24         def.reject("Failed to create");25     }26     27     return def;28   }29   30   request.get("crossdomain/ie/getData", {31     method: "get",32     data:{id:'ie9'}33   }).then(function(text){34     // Do something with the response35   });36 37 });
View Code

  以下便是registry的源码:

define([    'require',    '../_base/array',    './default!platform',//想想notify中的load函数    './util'], function(require, array, fallbackProvider, util){    var providers = [];    function request(url, options){        var matchers = providers.slice(0),//作用类似clone            i = 0,            matcher;        while(matcher=matchers[i++]){            if(matcher(url, options)){//匹配provider                return matcher.request.call(null, url, options);            }        }        //fallbackProvider由default根据不同平台注入默认的provider        return fallbackProvider.apply(null, arguments);    }    function createMatcher(match, provider){        var matcher;        if(provider){            if(match.test){                // RegExp                matcher = function(url){                    return match.test(url);                };            }else if(match.apply && match.call){                matcher = function(){                    return match.apply(null, arguments);                };            }else{                matcher = function(url){                    return url === match;                };            }            matcher.request = provider;        }else{            // If only one argument was passed, assume it is a provider function            // to apply unconditionally to all URLs            matcher = function(){                return true;            };            matcher.request = match;        }        return matcher;    }    request.register = function(url, provider, first){        var matcher = createMatcher(url, provider);        providers[(first ? 'unshift' : 'push')](matcher);        return {            remove: function(){                var idx;                if(~(idx = array.indexOf(providers, matcher))){                    providers.splice(idx, 1);                }            }        };    };    //这里意味着registry也可以使用插件的写法,作用是替换一个默认的provider    request.load = function(id, parentRequire, loaded, config){        if(id){            // if there's an id, load and set the fallback provider            require([id], function(fallback){                fallbackProvider = fallback;//js中的词法作用域,load中永远能够访问到fallbackProvider变量。                loaded(request);            });        }else{            loaded(request);        }    };    util.addCommonMethods(request);    return request;});
View Code

 

  handlers

  XMLHttpRequest对象请求成功后返回的数据格式只有text跟xml两种,handlers根据request中指定的handleAs参数将请求成功后的数据转化为指定类型。与jquery中的作用类似。

  dojo中提供了以下三种数据转化器:

  

  此外,handlers有跟registry类似的register方法,可以让我们自定义数据转化器。

1 require(["dojo/request/handlers", "dojo/request", "dojo/dom", "dojo/dom-construct", "dojo/json", 2     "dojo/on", "dojo/domReady!"], 3 function(handlers, request, dom, domConst, JSON, on){ 4   handlers.register("custom", function(response){ 5     var data = JSON.parse(response.text); 6     data.hello += "!"; 7     return data; 8   }); 9 10   on(dom.byId("startButton"), "click", function(){11     domConst.place("

Requesting...

", "output");12 request("./helloworld.json", {13 handleAs: "custom"14 }).then(function(data){15 domConst.place("

data: " + JSON.stringify(data) + "", "output");16 });17 });18 });

View Code

  

  如果您看完本篇文章感觉不错,请点击一下下方的推荐来支持一下博主,谢谢!

上一篇:dojo Provider(script、xhr、iframe)源码解析
下一篇:require、module、exports dojo中的三个特殊模块标识

发表评论

最新留言

关注你微信了!
[***.104.42.241]2025年04月03日 06时19分16秒