总体说明

  做前端当然少不了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自己来处理,一般情况下我们不需要关心。

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

  

  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模块整体架构解析的更多相关文章

  1. 特斯拉Tesla Model 3整体架构解析(下)

    特斯拉Tesla Model 3整体架构解析(中) Tesla Computer Unit 特斯拉已经开发了一个由自动驾驶仪和信息计算机组成的定制"液冷双计算平台"."他 ...

  2. 特斯拉Tesla Model 3整体架构解析(上)

    特斯拉Tesla Model 3整体架构解析(上) 一辆特斯拉 Model 3型车在硬件改造后解体 Sensors for ADAS applications 特斯拉 Model 3型设计的传感器组件 ...

  3. Spark技术内幕:Storage 模块整体架构

    Storage模块负责了Spark计算过程中所有的存储,包括基于Disk的和基于Memory的.用户在实际编程中,面对的是RDD,可以将RDD的数据通过调用org.apache.spark.rdd.R ...

  4. dojo/request

    dojo/request模块整体架构解析   总体说明 做前端当然少不了ajax的使用,使用dojo的童鞋都知道dojo是基于模块化管理的前端框架,其中对ajax的处理位于dojo/request模块 ...

  5. jQuery整体架构源码解析(转载)

    jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...

  6. jQuery整体架构源码解析

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  7. spring源码深度解析—Spring的整体架构和环境搭建

    概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框 ...

  8. Spring源码解析--Spring的整体架构

    概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框 ...

  9. 【Hadoop离线基础总结】流量日志分析网站整体架构模块开发

    目录 数据仓库设计 维度建模概述 维度建模的三种模式 本项目中数据仓库的设计 ETL开发 创建ODS层数据表 导入ODS层数据 生成ODS层明细宽表 统计分析开发 流量分析 受访分析 访客visit分 ...

随机推荐

  1. C语言实现 字符串过滤并修改并返回个数

    基本问题:给定一个strContent,strWord,使用strWord 匹配strContent,匹配成功,将匹配部分全部替换为‘*’ ,并返回匹配成功个数.注意不能使用库函数. 例如:strCo ...

  2. [python] 创建临时文件-tempfile模块

    This module generates temporary files and directories. It works on all supported platforms.In versio ...

  3. 一个Angular模块中可以声明哪些组件?

    一个Angular模块中可以声明哪些组件? (1) controller        控制器 (2) directive                指令 (3) function         ...

  4. SQl server 关于重复插入数据的测试

    最近发布的脚本,有那种防止重复插入数据(包括存在时更新,不存在是插入的处理,判断的方向可能与下面的示例相反) 使用类似下面的 SQL declare @id int, @value int if no ...

  5. php memcache扩展 出现错误dyld: Symbol not found: _mmc_queue_free

    mac 10.10 系统安装php memcache扩展 在使用memcache的时候出现错误dyld: Symbol not found: _mmc_queue_free需要重新编译memcache ...

  6. 用nexus搭建maven私服

    首先介绍一下背景,公司访问外网有限制,项目组大部分人员不能访问maven的central repository,因此在局域网里找一台有外网权限的机器,搭建nexus私服,然后开发人员连到这台私服上 环 ...

  7. 解决EasyUI动态添加标签渲染问题

    以下代码用于Js脚本中: var Work_Content_Back = "<table width='99%' class='table' style='margin-bottom: ...

  8. 在Windows上编译最新的CURL,含有zlib,openssl

    最近,从网上下载了一个curl库,使用时各种报错,都无法启动,于是干脆就直接自己编译了. 1. 准备工作 a. 下载zlib zlib可以使得HTTP请求支持gzip压缩,其地址如下: 官网:http ...

  9. [UCSD白板题] Longest Common Subsequence of Three Sequences

    Problem Introduction In this problem, your goal is to compute the length of a longest common subsequ ...

  10. Alice and Bob(2013年山东省第四届ACM大学生程序设计竞赛)

    Alice and Bob Time Limit: 1000ms   Memory limit: 65536K 题目描述 Alice and Bob like playing games very m ...