zepto源碼ajax模塊學習

在學習zepto的源碼的時候,不得不稱讚這些人的厲害,我雖然能看明白,可是要我寫,估計吭哧吭哧寫不出來。雖然如今不多人使用zepto了,可是學習這些源碼我相信每次看都能給我們不一樣的感覺。Deferred對象待會講,deferred我認爲是zepto設計最巧妙的地方。先來看zepto的ajax模塊。html

;(function($){
  var jsonpID = +new Date();

  function triggerAndReturn(context, eventName, data) {
    var event = $.Event(eventName);
    $(context).trigger(event, data);
    return !event.isDefaultPrevented();
  }
})

這裏爲何文件開頭都要使用';'這是由於在對多個js文件進行打包的時候,若是使用換行分隔代碼,當合並壓縮多個文件以後,換行符會被刪掉,連在一塊兒可能出錯,加上分號就保險了。
jsonpID在跨域jsonp的時候會使用到,這是爲了禁止使用cache。triggerAndReturn()的目的在於建立一個Event事件,而後在context觸發他,若是默認行爲被取消了,則返回false。ajax

$.ajaxSettings = {
  type: 'GET',
  success: empty,
  xhr: function () {
    return new window.XMLHttpRequest()
  },
  cache: true,
  crossDomain: false
}

這是ajax的初始化,默認是GET請求,xhr是新建的XMLHttpRequest()對象,cache表示瀏覽器是否應該被容許緩存GET響應。crossDomain表示是否能夠來自另一個域。
下面來看看最核心的$.ajax方法。正則表達式

$.ajax = function (options) {
      var settings = $.extend({}, options || {}),
        deferred = $.Deferred && $.Deferred(),
        urlAnchor, hashIndex
      for (key in $.ajaxSettings)
        if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]
      ajaxStart(settings)
    ...
  }

首先傳入options,而後將傳入的options存儲到本地,deferred我們暫時能夠把它當作一個promise對象,遍歷$.ajaxSettings,若是用戶沒有設置裏邊有的屬性,那就使用默認的屬性。而後調用ajaxStart開始的函數。json

if (!settings.crossDomain) {
  urlAnchor = document.createElement('a');
  urlAnchor.href = settings.url;
  urlAnchor.href = urlAnchor.href;
  settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host)
}

這裏首先crossDomain爲false的狀況下,進入該邏輯,經過判斷咱們傳入的url和當前window.location.href作對比,判斷是否是跨域。跨域

if ((hashIndex = settings.url.indexOf('#')) > -1) settings.url = settings.url.slice(0, hashIndex)

var dataType = settings.dataType,
  hasPlaceholder = /\?.+=\?/.test(settings.url);
if (hasPlaceholder) dataType = 'jsonp';

if (settings.cache === false || (
    (!options || options.cache !== true) &&
    ('script' == dataType || 'jsonp' == dataType)
  ))
  settings.url = appendQuery(settings.url, '_=' + Date.now())

function appendQuery(url, query) {
  if (query == '') return url;
  return (url + '&' + query).replace(/[&?]{1,2}/, '?')
}
if ('jsonp' == dataType) {
  if (!hasPlaceholder)
    settings.url = appendQuery(settings.url,
      settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?')
  return $.ajaxJSONP(settings, deferred)
}

若是咱們沒有傳入url,那麼url就是window.location.href。'#'表明網頁中的一個位置,右邊的字符,就是該位置的標識符,#是用來指導瀏覽器動做的,對瀏覽器沒有用,因此在截取url的時候,不必把後面的部分傳給服務器。hasPlaceholder這裏的正則表達式,用於匹配相似'?name=?'這種字符串,若是hasPlaceholder爲true,則dataType爲jsonp。下面的時不使用留在緩存的數據,第一種是設置cache爲false,或者dataType爲script和jsonp的狀況,須要在url後面添加事件。下面是appendQuery方法,就是把字符串拼接到url後邊,可是須要把'&'替換成'?'。若是咱們的請求是jsonp請求,須要在url後面添加一些callback=?這種參數。數組

var mime = settings.accepts[dataType],
    headers = {},
    setHeader = function(name, value) {headers[name.toLowerCase()] = [name, value]},
    protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
    xhr = settings.xhr(),
    nativeSetHeader = xhr.setRequestHeader,
    abortTimeout

if (deferred) deferred.promise(xhr);

if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest');
setHeader('Accept', mime || '*/*')
if (mime = settings.mimeType || mime) {
  if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0]
  xhr.overrideMimeType && xhr.overrideMimeType(mime)
}

settings中的accepts表示從服務器請求的MIME類型,指定dataType的值,包括script、json、xml、html、text。mime存儲的就是相似'application/json'的字符串。protocol就是匹配我們相似'http://'的這種協議。
若是deferred存在,就把xhr轉換爲deferred對象。接着看下去,若是不是跨域的,那就是ajax請求。而後後面的判斷條件查了一下是針對一些mozillar瀏覽器進行修正(有個問題就是他們上哪兒知道的用這種方式來修正啊)。promise

xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    xhr.onreadystatechange = empty
    if (/*若是成功*/){
      // 對返回結果進行處理
    }
  }
}

xhr.open(settings.type, settings.url, async, settings.username, settings.password)

沒什麼難的,就是根據傳入的參數不一樣進行處理。瀏覽器

$.param

這個函數的做用在於序列化傳入對象,下面是他的代碼:緩存

$.param = function (obj, traditional) {
   var params = []
   params.add = function (key, value) {
     if ($.isFunction(value)) value = value()
     if (value == null) value = ""
     this.push(escape(key) + '=' + escape(value))
   }
   serialize(params, obj, traditional)
   return params.join('&').replace(/%20/g, '+')
 }

傳入一個對象,和一個標記,這個traditional表示激活傳統的方式經過$.param來獲得data。首先定義一個空數組,若是在上面添加方法,這個方法的主要做用向params裏邊添加序列化的對象,escape=encodeURIComponent,而後調用serialize,將obj對象添加到params中,最後返回將params數組用'&'拼接,而後這裏的%20表示空格,意思是將空格替換成'+'服務器

serialize

下面是代碼

function serialize(params, obj, traditional, scope) {
  var type, array = $.isArray(obj),
    hash = $.isPlainObject(obj)
  $.each(obj, function (key, value) {
    type = $.type(value)
    if (scope) key = traditional ? scope :
      scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']'
    // handle data in serializeArray() format
    if (!scope && array) params.add(value.name, value.value)
    // recurse into nested objects
    else if (type == "array" || (!traditional && type == "object"))
      serialize(params, value, traditional, key)
    else params.add(key, value)
  })
}

params是帶有add方法的數組,遍歷obj的鍵值對,屬性值多是對象也多是數組或者字符串分別進行處理。若是obj的某個屬性值是對象或者數組,scope就表明是該屬性,那麼這時候向params傳入的key就須要變化,變成相似'a[b]'這裏的b是obj的某個屬性值是對象的一個屬性。

相關文章
相關標籤/搜索