dojo/request模塊總體架構解析

  整體說明css

  作前端固然少不了ajax的使用,使用dojo的童鞋都知道dojo是基於模塊化管理的前端框架,其中對ajax的處理位於dojo/request模塊。通常狀況下咱們使用ajax請求只須要引入dojo/request模塊,而後按照文檔的說明制定參數便可。實際上dojo在這一模塊的處理中抽象了不少概念:html

  • 平臺偵探器: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請求上可以提供強大的擴展性和簡捷的接口。node

 

  Providerjquery

  請求傳輸器被稱爲Provider,dojo框架自身提供瞭如下4個providerweb

  • 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狀態的值。這個對象有如下幾個屬性:ajax

  關於這幾個Provider的詳細講解請繼續關注下一篇文章正則表達式

  

  defaultjson

  通常狀況下咱們發送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 });
View Code

  代碼中關於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 });
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 response
35  }); 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("<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 });
View Code

  

  若是您看完本篇文章感受不錯,請點擊一下下方的推薦來支持一下博主,謝謝!

相關文章
相關標籤/搜索