zoukankan      html  css  js  c++  java
  • dojo/request

    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。

     View Code
     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的事件。

     View Code
    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。

     View Code
    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的源码:

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

     View Code
    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 });

      

     
    dojo/request模块整体架构解析 木的树 2015-04-19 18:22 阅读:262 评论:3  
     
    require、module、exports dojo中的三个特殊模块标识 木的树 2015-04-17 00:04 阅读:286 评论:2  
     
    Javascript图片裁切 木的树 2015-04-05 19:26 阅读:603 评论:6  
     
    TypeError: 'bases' is null or not an object。IE8 bug 腐朽的对象 木的树 2014-05-28 13:27 阅读:111 评论:0  
     
    前端自动化测试工具doh学习总结(二) 木的树 2014-05-17 23:55 阅读:775 评论:3  
     
    前端自动化测试工具doh学习总结(一) 木的树 2014-05-14 23:59 阅读:655 评论:2  
     
    dojo + jersey 上传图片到数据库 木的树 2013-03-27 15:00 阅读:1821 评论:6  
     
    dojo 图表初步 木的树 2012-07-10 21:47 阅读:2985 评论:1  
     
    dojo可视化编程工具 木的树 2012-06-19 15:38 阅读:392 评论:0  
     
    让WebStorm支持dojo的智能提示 木的树 2012-06-14 10:36 阅读:2655 评论:5  
     
    dojo从asp.net中获取json数据 木的树 2012-06-03 22:13 阅读:405 评论:2  
     
    dojo在ASP.NET中使用Ajax初步 木的树 2012-05-24 08:44 阅读:510 评论:0  
     
     
  • 相关阅读:
    怎样评价海贼王中艾斯的死?
    interleaving-string
    打败微信的将是怎么样的一款产品?
    javascript实例——文本特效篇(包含3个小例子)
    javascript实例——时间日期篇(包含5个实例)
    【转】Js获取当前日期时间及其它操作
    【转】外国朋友出的js题目,你能对几道
    【转】30+有用的CSS代码片段
    php的ajax简单实例
    Border属性的各种变化
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4442961.html
Copyright © 2011-2022 走看看