dojo/request模块整体架构解析
总体说明
做前端当然少不了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返回一个promise对象,其中有一个不在标准规范内的属性:response。该属性是一个标准promise对象,该对象将一个代表服务器端响应结果的对象作为fulfill状态的值。这个对象有以下几个属性:



关于这几个Provider的详细讲解请继续关注下一篇文章
default
一般情况下我们发送ajax请求时只需引入dojo/request即可,实际上这是在default中根据不同的运行平台自动给我们提供了一个默认的provider。
define([
'exports',
'require',
'../has'
], function(exports, require, has){
//读取dojoConfig中的配置信息
var defId = has('config-requestProvider'),
platformId; //根据不同平台选择不同的provider
if(has('host-browser') || has('host-webworker')){
platformId = './xhr';
}else if(has('host-node')){
platformId = './node';
/* TODO:
}else if(has('host-rhino')){
platformId = './rhino';
*/
} if(!defId){
defId = platformId;
} exports.getPlatformDefaultId = function(){
return platformId;
};
//作为插件使用,是跟参数选择provider
exports.load = function(id, parentRequire, loaded, config){
require([id == 'platform' ? platformId : defId], function(provider){
loaded(provider);
});
};
});
代码中关于exports跟require模块的说明请看我的上一篇博客:require、module、exports dojo中的三个特殊模块标识。
上述内容关于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自己来处理,一般情况下我们不需要关心。
notify
notify是全局的ajax事件通知器,负责全局范围内的ajax事件监听,有类似于jquery中ajaxStart、ajaxComplete的事件。
 define(['../Evented', '../_base/lang', './util'], function(Evented, lang, util){
     // module:
     //        dojo/request/notify
     // summary:
     //        Global notification API for dojo/request. Notifications will
     //        only be emitted if this module is required.
     //
     //        | require('dojo/request', 'dojo/request/notify',
     //        |     function(request, notify){
     //        |         notify('load', function(response){
     //        |             if(response.url === 'someUrl.html'){
     //        |                 console.log('Loaded!');
     //        |             }
     //        |         });
     //        |         request.get('someUrl.html');
     //        |     }
     //        | );
     var pubCount = 0,
         slice = [].slice;
     //实例化dojo/Evented对象,负责分发事件
     var hub = lang.mixin(new Evented, {
         onsend: function(data){
             if(!pubCount){
                 this.emit('start');
             }
             pubCount++;
         },
         _onload: function(data){
             this.emit('done', data);
         },
         _onerror: function(data){
             this.emit('done', data);
         },
         _ondone: function(data){
             if(--pubCount <= 0){
                 pubCount = 0;
                 this.emit('stop');
             }
         },
         emit: function(type, event){
             var result = Evented.prototype.emit.apply(this, arguments);
             // After all event handlers have run, run _on* handler
             //运行完标准事件处理函数后,再来运行本身的私有函数。
             //load和error事件处理完后触发done事件
             //done事件处理完毕后,再来运行本身的_ondone函数,然后触发stop事件
             if(this['_on' + type]){
                 this['_on' + type].apply(this, slice.call(arguments, 1));
             }
             return result;
         }
     });
     function notify(type, listener){
         // summary:
         //        Register a listener to be notified when an event
         //        in dojo/request happens.
         // type: String?
         //        The event to listen for. Events emitted: "start", "send",
         //        "load", "error", "done", "stop".
         // listener: Function?
         //        A callback to be run when an event happens.
         // returns:
         //        A signal object that can be used to cancel the listener.
         //        If remove() is called on this signal object, it will
         //        stop the listener from being executed.
         return hub.on(type, listener);
     }
     notify.emit = function(type, event, cancel){
         return hub.emit(type, event, cancel);
     };
     // Attach notify to dojo/request/util to avoid
     // try{ require('./notify'); }catch(e){}
     return util.notify = notify;
 });
最后的一句:util.notify= notify; util将notify与provider关联起来。
registry
该模块可以在不同的情况下使用不同的provider;匹配的条件可以是正则表达式、字符串或者函数。通过registry可以根据不同的条件注册不同的provider。
 require(["dojo/request/registry", "dojo/Deferred"], function(request, Deferred){
   request.register("crossdomain/ie", xdrProvider);
   var xdrProvider = function(url, options){
     var def = new Deferred();
     xdr = new XDomainRequest();
     if (xdr) {
       xdr.onerror = function(){
         def.reject('error');
       };
       xdr.ontimeout = function(){
         def.reject('timeout');
       };
       xdr.onprogress = function(){
         def.progress('progress');
       };
       xdr.onload = function(res){
         def.resolve(res);
       };
       xdr.timeout = 6000;
       xdr.open(options.method, url);
       xdr.send(serilize(options.data));
     } else {
         def.reject("Failed to create");
     }
     return def;
   }
   request.get("crossdomain/ie/getData", {
     method: "get",
     data:{id:'ie9'}
   }).then(function(text){
     // Do something with the response
   });
 });
以下便是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;
});
handlers
XMLHttpRequest对象请求成功后返回的数据格式只有text跟xml两种,handlers根据request中指定的handleAs参数将请求成功后的数据转化为指定类型。与jquery中的类型转化器作用类似。
dojo中提供了以下三种数据转化器:
  
此外,handlers有跟registry类似的register方法,可以让我们自定义数据转化器。
require(["dojo/request/handlers", "dojo/request", "dojo/dom", "dojo/dom-construct", "dojo/json",
"dojo/on", "dojo/domReady!"],
function(handlers, request, dom, domConst, JSON, on){
handlers.register("custom", function(response){
var data = JSON.parse(response.text);
data.hello += "!";
return data;
}); on(dom.byId("startButton"), "click", function(){
domConst.place("<p>Requesting...</p>", "output");
request("./helloworld.json", {
handleAs: "custom"
}).then(function(data){
domConst.place("<p>data: <code>" + JSON.stringify(data) + "</code>", "output");
});
});
});
如果您看完本篇文章感觉不错,请点击一下下方的推荐来支持一下博主,谢谢!
dojo/request模块整体架构解析的更多相关文章
- 特斯拉Tesla Model 3整体架构解析(下)
		特斯拉Tesla Model 3整体架构解析(中) Tesla Computer Unit 特斯拉已经开发了一个由自动驾驶仪和信息计算机组成的定制"液冷双计算平台"."他 ... 
- 特斯拉Tesla Model 3整体架构解析(上)
		特斯拉Tesla Model 3整体架构解析(上) 一辆特斯拉 Model 3型车在硬件改造后解体 Sensors for ADAS applications 特斯拉 Model 3型设计的传感器组件 ... 
- Spark技术内幕:Storage 模块整体架构
		Storage模块负责了Spark计算过程中所有的存储,包括基于Disk的和基于Memory的.用户在实际编程中,面对的是RDD,可以将RDD的数据通过调用org.apache.spark.rdd.R ... 
- dojo/request
		dojo/request模块整体架构解析 总体说明 做前端当然少不了ajax的使用,使用dojo的童鞋都知道dojo是基于模块化管理的前端框架,其中对ajax的处理位于dojo/request模块 ... 
- jQuery整体架构源码解析(转载)
		jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ... 
- jQuery整体架构源码解析
		最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ... 
- spring源码深度解析—Spring的整体架构和环境搭建
		概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框 ... 
- Spring源码解析--Spring的整体架构
		概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框 ... 
- 【Hadoop离线基础总结】流量日志分析网站整体架构模块开发
		目录 数据仓库设计 维度建模概述 维度建模的三种模式 本项目中数据仓库的设计 ETL开发 创建ODS层数据表 导入ODS层数据 生成ODS层明细宽表 统计分析开发 流量分析 受访分析 访客visit分 ... 
随机推荐
- 浅谈php设计模式(1)---工厂模式
			一.接口继承直接调用 先看看这样一段代码: <?php interface db{ function conn(); } class dbmysql implements db { public ... 
- 使用odoo价格表[pricelist]对价格进行特别处理,如 .99
			问题1, 销售价格 自动设置为 9.99 问题2, 将销售价格从 10.5 设置为 10.00 这些都可以用 priceList 实现,具体实现如下 首先,启用 priceList 在 价格表项目 有 ... 
- 一起买Beta版本系列文档
			一起买beta版本文档报告汇总 031402401鲍亮 031402402曹鑫杰 031402403常松 031402412林淋 031402418汪培侨 031402426许秋鑫 一.Beta版本冲 ... 
- Spring知识点总结大全(1)
			1.Spring的分层结构 1.Presentation layer(表示层) (1) 表示逻辑(生成界面代码) (2) 接收请求 (3) 处理业务层抛出的异常 (4) 负责规则验证(数据格式,数据非 ... 
- freeCodeCamp:Caesars Cipher
			让上帝的归上帝,凯撒的归凯撒. 下面我们来介绍风靡全球的凯撒密码Caesar cipher,又叫移位密码. 移位密码也就是密码中的字母会按照指定的数量来做移位. 一个常见的案例就是ROT13密码,字母 ... 
- bootstrap 模态框禁用空白处点击关闭
			bootstrap 3 模态框为信息编辑窗口,默认点击空白处会自动关闭,但是有的时候这个功能需要关闭,比如编辑信息,一不小心点击了空白处,那就要重新编辑了. bootstrap 3 模态框禁用空白处点 ... 
- 动画总结(UIView的动画)
			Main.storyboard ViewController.m // // ViewController.m // 8A08.动画总结 // // Created by huan on 16/ ... 
- express教程
			原文地址 http://javascript.ruanyifeng.com/nodejs/express.html JavaScript 标准参考教程(alpha) 草稿二:Node.js Expre ... 
- Angular概念纵览
			Conceptual Overview Template(模板): HTML with additional markup (就是增加了新的标记的HTML) Directive(指令): extend ... 
- what is a ear
			http://docs.oracle.com/javaee/6/tutorial/doc/bnaby.html An EAR file (see Figure 1-6) contains Java E ... 
