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。

 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     //根据不同平台选择不同的provider
11     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     //作为插件使用,是跟参数选择provider
29     exports.load = function(id, parentRequire, loaded, config){
30         require([id == ‘platform‘ ? platformId : defId], function(provider){
31             loaded(provider);
32         });
33     };
34 });

  代码中关于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的事件。

 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* handler
45             //运行完标准事件处理函数后,再来运行本身的私有函数。
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 event
58         //        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 will
67         //        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 avoid
75     // try{ require(‘./notify‘); }catch(e){}
76     return util.notify = notify;
77 });

  最后的一句: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 response
35   });
36
37 });

  以下便是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方法,可以让我们自定义数据转化器。

 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("<p>Requesting...</p>", "output");
12     request("./helloworld.json", {
13       handleAs: "custom"
14     }).then(function(data){
15       domConst.place("<p>data: <code>" + JSON.stringify(data) + "</code>", "output");
16     });
17   });
18 });

  

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

时间: 2024-10-21 00:43:23

dojo/request模块整体架构解析的相关文章

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

Storage模块负责了Spark计算过程中所有的存储,包括基于Disk的和基于Memory的.用户在实际编程中,面对的是RDD,可以将RDD的数据通过调用org.apache.spark.rdd.RDD#cache将数据持久化:持久化的动作都是由Storage模块完成的.包括Shuffle过程中的数据,也都是由Storage模块管理的.可以说,RDD实现了用户的逻辑,而Storage则管理了用户的数据.本章将讲解Storage模块的实现. 1.1     模块整体架构 org.apache.s

1.1 Spring的整体架构--Spring源码深度解析

前言: Spring 始于2003年,轻量级 Java 开源框架. Spring 是为了解决企业应用开发的复杂性而创建的,它使用基本的 JavaBean 来完成以前只可能由 EJB 完成的事情. Spring 的用途不仅限于服务器端的开发,从简单性.可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益. Spring 的整体架构: Spring 框架是一个分层架构,被分为大约20个模块. (1)Core Container Core Container (核心容器)包含

jQuery整体架构源码解析

最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹. 另外,阅读源码让我接触到了大量底层的知识.对原生JS .框架设计.代码优化有了全新的认识,接下来将会写一系列关于 jQuery 解析的文章. 我在 github 上关于 jQuery 源码的全文注解,感兴趣的可以围观一下

【Spring源码深度解析系列 】Spring整体架构

一.Spring的整体架构和模块 二.模块分类: 1.Core Container Core Container包含有Core .Beans.Context.和Expression  Language模块 2.Data Access/Integration Data Access/Integration包含有JDBC.ORM.OXM.JMS和Transaction模块 3.Web Web层包含了Web.Web-Servlet.Web-Struts.Web-Porlet模块. 4.AOP 5.Te

tomcat原理解析(二):整体架构

一 整体结构 前面tomcat实现原理(一)里面描述了整个tomcat接受一个http请求的简单处理,这里面我们讲下整个tomcat的架构,以便对整体结构有宏观的了解.tomat里面由很多个容器结合在一起,主要有server,service,context,host,engine,wrapper,connector这7个容器来组装.当然了tomcat里面还有其它容器这里就不一一列举,因为我只看重点的.这7个容器存着父子关系,即可以通过当前容器找自己的父容器和自己的子容器.说到这我画了一个简单的结

OpenStack监控架构解析

1  采集模块整体架构 采集模块主要分为三大块. Ceilometer:用于采集数据并处理数据后发送到gnocchi服务去存储 Gnocchi:用于将采集数据进行计算合并和存储并提供rest api方式接收和查询监控数据 Aodh:主要负责告警功能 1.1  Ceilometer架构 Ceilometer-polling服务:通过调用多个采集插件(采集插件在setup.cfg中有定义,ceilometer.poll.compute对应的就是采集插件)收集信息,这个服务收集的是有关虚拟机资源使用情

新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析(阿里)

1.引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件.整体架构,知其然且知其所以然,希望给大家在实际开发实践.学习开源项目方面提供参考. 本文作者的另两篇<高性能网络编程(五):一文读懂高性能网络编程中的I/O模型>.<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>也写的很好,有兴趣的读者可以一并看看. 关于作者: 陈彩华(

Tomcat系列(一)- 整体架构

整体架构 我们想要了解一个框架,首先要了解它是干什么的,Tomcat我们都知道,是用于处理连接过来的Socket请求的.那么Tomcat就会有两个功能: 对外处理连接,将收到的字节流转化为自己想要的Request和Response对象 对内处理Servlet,将对应的Request请求分发到相应的Servlet中 那么我们整体的骨架就出来了,Tomcat其实就分为两大部分,一部分是连接器(Connnector)处理对外连接和容器(Container)管理对内的Servelet. 大体的关系图如下

Tomcat是如何运行的?整体架构又是怎样的?

在许多的高端开发的岗位中都会或多或少有要求面试人员要研究过一些常用中间件源码.这是因为一切的秘密都是藏在源码中,阅读源码能够让我们对框架或者中间件的理解更加深刻,而我们也能够在源码的研究中获得其中一些优秀的设计方式.而我们的中间件和源码那么多,我们该从何入手呢?其实大部分的中间件或者框架都有一些共性的部分,例如网络编程.多线程.反射和类加载等技术.所以深入研究透了一两个中间件的话,那么再回过头来看其它的中间件,那么就会很容易理解它里面所用的技术以及原理.而作为一个老牌的WEB端框架Tomcat,