zepto源碼學習-05 ajax

學習zeptoajax以前須要先腦補下,強烈推薦此文http://www.cnblogs.com/heyuquan/archive/2013/05/13/js-jquery-ajax.htmljavascript

還有Aaron 的jquery源碼分析系列,講jquery的ajax的部分,固然其餘也能夠看,很值得學習。php

zepto ajax部分的設計相對簡單,畢竟zepto就是輕量級的庫,沒有jqeury那樣複雜,jquery ajax是依賴於Deferred模塊的,整個代碼一千多行。而zepto只有幾百行,總體設計相對簡單,ajax部分能夠不依賴於Deferred模塊,獨立運行。zepto ajax的使用相見 地址,先看懂使用而後再分析具體實現。
css

先看一個demohtml

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Ajax</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
    <meta content="telephone=no" name="format-detection">
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <meta name="description" content="">
    <meta name="keywords" content="">
    <style type="text/css" media="screen">
        button{
            width:150px;
            height:100px;
            line-height:100px;
            margin:5px;
        }
    </style>
</head>
<body>
    <button type="" id="btnTest">testajax</button>
    <script type="text/javascript" src="../zepto-full-1.1.6.js"></script>
    <script>
        // 全局事件
        $(document).on('ajaxStart',function (e) {
                log('觸發ajaxStart回調函數');
        }).on('ajaxSend',function (e) {
                log('觸發ajaxSend回調函數');
        }).on('ajaxBeforeSend',function (e) {
                log('觸發ajaxBeforeSend回調函數');
        }).on('ajaxSuccess',function (e, jqXHR, s, data) {
                log('觸發ajaxSuccess回調函數');
        }).on('ajaxError',function (e, jqXHR, s, errorData) {
                log('觸發ajaxError回調函數');
        }).on('ajaxComplete',function (e, jqXHR, s) {
                log('觸發ajaxComplete回調函數');
        }).on('ajaxStop',function (e) {
                log('觸發ajaxStop回調函數');
        });

        $('#btnTest').on('click',bindLocalEvent);
        // 局部事件
        function bindLocalEvent(e) {
                var textareaid ='txt_event';             
                $.ajax({
                //跨域地址http://192.168.11.198:8080/myTest/test/ajax-page.php
                //ajax.php
                url:'ajax.php',//ajax.php
                            type: 'get',
                            dataType: 'json',//text、json、jsonp
                data:{
                    msg:'testtest',
                    error:3
                },
                        //global: true,
                        //cache: false,
                //jsonpCallback:log2,
                        beforeSend: function (jqXHR, s) {
                            log('觸發beforeSend回調函數');
                        },
                //zepto 不支持
                        dataFilter: function (data, dataType) {
                            log('觸發dataFilter回調函數');
                    return data;
                        },
                        success: function (data, statusText, jqXHR) {
                                log('觸發success回調函數');
                        },
                        error: function (jqXHR, textStatus, errorThrown) {
                                log('觸發error回調函數');
                        },
                        complete: function (jqXHR, textStatus) {
                                log('觸發complete回調函數');
                        }}).done(function(){
                    console.log('觸發ajax done',this,[].slice.call(arguments));
                });
        }

        function log(txt) {
            console.log(txt);
        }
        function log2(txt) {
            console.log('------------jsonpCallback------------------');
            //return function name
            return 'log3';
        }
        function log3(txt) {
            console.log('------------jsonpCallback done------------------');
        }
    </script>
</body>
</html>
ajax demo

點擊按鈕,控制檯輸出以下java

 以上圖片基本上展現了正常狀況下zepto ajax的處理流程,測試代碼我是引入了Deferred模塊的,因此能夠鏈式調用done(後續會說)。jquery

XHR那行輸出是我默認開啓chrome的Log XMLHttpRequests,全部的XMLHttpRequests都會在控制檯輸出。web

再看一個發生錯誤的輸出,一旦請求發生錯誤,後續的處理流程將發生變化。ajax

 再看跨域時候的狀況,這個測試我指定了jsonpCallback爲log2,dataType爲jsonp,地址爲http://192.168.11.198:8080/myTest/test/ajax-page.php。ajax請求部分參數以下chrome

url:'http://192.168.11.198:8080/myTest/test/ajax-page.php',//ajax.php
            type: 'get',
            dataType: 'jsonp',//text、json、jsonp
data:{
    msg:'testtest',
    error:3
},
jsonpCallback:log2,

對應的php代碼以下json

<?php
$status = $_REQUEST['error'] ? 1 : 0;
$message = $status == 1 ? "驗證碼好像不對哦" : "SUCCESS";
if (!empty($_REQUEST['msg'])) {
    $msg=$_REQUEST['msg'];
        $message = $msg;
        $status  = $_REQUEST['error'];
 }
$d=array("result"=>$status,"message"=>$message,"status"=>$_REQUEST['error']);
$c=$_REQUEST['callback'];
$rd;
if ($c===null || $c==='') {
    echo json_encode($d);
}else{
    $rd=$c+"("+json_encode($d)+")";
    echo  $c."(".json_encode($d).")";
}
?>

php代碼主要是取到callback,最後把數據組裝爲  callback(data)的形勢返回。

此時頁面控制檯輸入以下

$.get、$.getJSON、$.post、load最後都調用了$.ajax,

$.ajax(options) 內部處理流程。

把$.ajaxSettings[key]中的設置賦值到options中

執行全局ajaxStart(settings)

判斷當前請求的url是否跨域

if (!settings.crossDomain) {
    urlAnchor = document.createElement('a')
    urlAnchor.href = settings.url
    urlAnchor.href = urlAnchor.href
        //內部判斷當前的url是否跨域
    settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host)
}

沒有指定url默認給定當前頁面url===》serializeData(settings)處理settings裏面的 data,其實就是對data作相關轉換

再次根據url判斷dataType的類型,若是dataType是jsonp類型,就執行$.ajaxJSONP,而且返回,稍後看ajaxJSONP的實現

var dataType = settings.dataType,
    //若是請求的是jsonp,則將地址欄裏的=?替換爲callback=?,至關於一個簡寫
    hasPlaceholder = /\?.+=\?/.test(settings.url)
if (hasPlaceholder) dataType = 'jsonp'

if (settings.cache === false || (
        (!options || options.cache !== true) &&
        ('script' == dataType || 'jsonp' == dataType)
    ))
//不緩存,在url後面添加時間戳
    settings.url = appendQuery(settings.url, '_=' + Date.now())

//針對jsonp進行處理
if ('jsonp' == dataType) {
    if (!hasPlaceholder)
        settings.url = appendQuery(settings.url, settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?')
    return $.ajaxJSONP(settings, deferred)
}

 

 if (deferred) deferred.promise(xhr) 若是引入了Deferred這裏會執行deferred.promise(xhr)。這句話的裏面實際上是

promise: function(obj) {
    return obj != null ? $.extend(obj, promise) : promise
}

把當前deferred的一些行爲附加到了xhr對象上,經過 deferred.promise() 方法返回的 deferred promise 對象,是沒有 resolve ,reject, progress , resolveWith, rejectWith , progressWith 這些能夠改變狀態的方法,只能使用 done, then ,fail 等方法添加 handler 或者判斷狀態。因此返回xhr對象後面只能鏈式調用 done, then ,fail,且不能改變當前Deferred的狀態。那何時改變Deferred的狀態呢,實際上是內部執行ajaxSuccess、ajaxError方法時,內部deferred對象調用resolveWith、rejectWith,改變deferred的狀態,一個執行成功resolveWith,一個執行失敗rejectWith。

function ajaxSuccess(data, xhr, settings, deferred) {
        var context = settings.context,
            status = 'success'
            //獲取上下文,調用success
        settings.success.call(context, data, status, xhr)
            //若是引用了Deferred模塊,這裏執行成功的通知,並傳遞相關參數
        if (deferred) deferred.resolveWith(context, [data, status, xhr])
            //觸發全局的成功
        triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
            //調用ajaxComplete
        ajaxComplete(status, xhr, settings)
    }
    // type: "timeout", "error", "abort", "parsererror"
function ajaxError(error, type, xhr, settings, deferred) {
    var context = settings.context
        //獲取上下文,調用error
    settings.error.call(context, xhr, type, error)
        //若是引用了Deferred模塊,這裏執行失敗的通知,並傳遞相關參數
    if (deferred) deferred.rejectWith(context, [xhr, type, error])
        //觸發全局的失敗
    triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type])
        //調用ajaxComplete
    ajaxComplete(type, xhr, settings)
}

若是咱們設置了超時時間、超時以後就會調用xhr.abort取消異步請求、並觸發ajaxError

//當設置了settings.timeout,則在超時後取消請求,並執行timeout事件處理函數,並處罰error
if (settings.timeout > 0) abortTimeout = setTimeout(function() {
    xhr.onreadystatechange = empty
    xhr.abort()
    ajaxError(null, 'timeout', xhr, settings, deferred)
}, settings.timeout)

xhr.readyState == 4,readyState有五種狀態:、0一、二、三、4;當值爲4的時候,表示數據接收完畢,此時能夠經過responseXml和responseText獲取完整的迴應數據。

判斷處理狀態,拿到相關數據,調用相關的處理函數。

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
        xhr.onreadystatechange = empty
            //清楚setTimeout
        clearTimeout(abortTimeout)
        var result, error = false
            //根據狀態來判斷請求是否成功
            //狀態>=200 && < 300 表示成功
            //狀態 == 304 表示文件未改動過,也可認爲成功
            //若是是取要本地文件那也能夠認爲是成功的,xhr.status == 0是在直接打開頁面時發生請求時出現的狀態,也就是否是用localhost的形式訪問的頁面的狀況
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
            //取到dataType
            dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'))
                //取到返回的數據
            result = xhr.responseText

            try {
                // http://perfectionkills.com/global-eval-what-are-the-options/
                if (dataType == 'script')(1, eval)(result)
                else if (dataType == 'xml') result = xhr.responseXML
                else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
            } catch (e) {
                error = e
            }
            //調用ajaxError
            if (error) ajaxError(error, 'parsererror', xhr, settings, deferred)
            else ajaxSuccess(result, xhr, settings, deferred)
        } else {
            ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred)
        }
    }
}

 

$.ajaxJSONP、jsonp的處理

內部處理就是建立一個script標籤去請求,對傳入的callback作了一些處理,重寫了callback

window[callbackName] = function() {
  responseData = arguments
}

最終執行的時候,先是拿到返回的數據,賦值給內部變量responseData。在load或者error的時候,執行響應函數,先是移除建立script對象上的事件數據,而後從頁面上移除這個對象。而後調用調用ajaxError或者ajaxSuccess,最後調用最初傳入的callback 

$.ajaxJSONP = function(options, deferred) {
    //沒有指定type
    if (!('type' in options)) return $.ajax(options)

    var _callbackName = options.jsonpCallback,
        //調用options.jsonpCallback,獲得返回的字符串。
        //若是沒有任何返回就生成一個callbackName=jsonpXXX.
        callbackName = ($.isFunction(_callbackName) ?
            _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)),
        //建立一個script標籤,這裏貌似沒有jq的簡潔啊
        script = document.createElement('script'),
        //保存最初指定的window[callbackName] callback
        //若是調用options.jsonpCallback,但都沒返回這裏就是隨機生成的jsonpXXX函數名,window[callbackName]應該是不存在的
        originalCallback = window[callbackName],
        responseData,
        abort = function(errorType) {
            $(script).triggerHandler('error', errorType || 'abort')
        },
        xhr = {
            abort: abort
        },
        abortTimeout

    if (deferred) deferred.promise(xhr)

    $(script).on('load error', function(e, errorType) {
        clearTimeout(abortTimeout)
            //銷燬事件,移除元素
        $(script).off().remove()

        if (e.type == 'error' || !responseData) {
            ajaxError(null, errorType || 'error', xhr, options, deferred)
        } else {
            ajaxSuccess(responseData[0], xhr, options, deferred)
        }
        //賦值回原來的callback
        window[callbackName] = originalCallback
            //最初保存的window[callbackName]
        if (responseData && $.isFunction(originalCallback))
          //調用
            originalCallback(responseData[0])
            //銷燬
        originalCallback = responseData = undefined
    })

    if (ajaxBeforeSend(xhr, options) === false) {
        abort('abort')
        return xhr
    }
    //生成的jsonpXXX函數,指定callback=jsonpXXX。 請求成功返回,就會調用這個函數,給responseData賦值
    //以前originalCallback = window[callbackName] 保存過了,這裏從新指定
    window[callbackName] = function() {
            responseData = arguments
        }
        //處理簡寫 callback的狀況
    script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName)
        //append到頁面上
    document.head.appendChild(script)
        //處理超時的狀況
    if (options.timeout > 0) abortTimeout = setTimeout(function() {
        abort('timeout')
    }, options.timeout)

    return xhr
}

  代碼簡單,看完api,而後組合相關狀況去測試,打斷點一步一步看內部的處理,不少還得本身去領悟。最後附上所有註釋代碼

  1 ;(function($) {
  2     var jsonpID = 0,
  3         document = window.document,
  4         key,
  5         name,
  6         rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
  7         scriptTypeRE = /^(?:text|application)\/javascript/i,
  8         xmlTypeRE = /^(?:text|application)\/xml/i,
  9         jsonType = 'application/json',
 10         htmlType = 'text/html',
 11         blankRE = /^\s*$/,
 12         originAnchor = document.createElement('a')
 13 
 14     originAnchor.href = window.location.href
 15         /*
 16         觸發ajaxStart回調函數 testAjax.html:109
 17         觸發beforeSend回調函數 testAjax.html:109
 18         觸發ajaxBeforeSend回調函數 testAjax.html:109
 19         觸發ajaxSend回調函數 testAjax.html:109
 20         XHR finished loading: GET "http://192.168.11.198:8080/sourceCode/zepto/testPage/ajax.php?_=1420612854381". zepto-full-1.1.6.js:1403
 21         觸發success回調函數 testAjax.html:109
 22         觸發ajax done
 23         觸發ajaxSuccess回調函數 testAjax.html:109
 24         觸發complete回調函數 testAjax.html:109
 25         觸發ajaxComplete回調函數 testAjax.html:109
 26         觸發ajaxStop回調函數
 27          */
 28         // trigger a custom event and return false if it was cancelled
 29     function triggerAndReturn(context, eventName, data) {
 30             //初始化一個Event對象
 31             var event = $.Event(eventName)
 32                 //觸發context這個對象的該事件
 33             $(context).trigger(event, data)
 34                 //返回是否調用過阻止默認行爲,初始爲false,掉用過就是true
 35             return !event.isDefaultPrevented()
 36         }
 37         //觸發 ajax的全局事件
 38         // trigger an Ajax "global" event
 39     function triggerGlobal(settings, context, eventName, data) {
 40         //默認context爲document
 41         if (settings.global) return triggerAndReturn(context || document, eventName, data)
 42     }
 43 
 44     // Number of active Ajax requests
 45     $.active = 0
 46 
 47     function ajaxStart(settings) {
 48         //設置了global 爲true
 49         if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart')
 50     }
 51 
 52     function ajaxStop(settings) {
 53         if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop')
 54     }
 55 
 56     // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
 57     function ajaxBeforeSend(xhr, settings) {
 58         var context = settings.context
 59             //先觸發beforeSend 再觸發ajaxBeforeSend ,其中一個返回false就中止往下執行
 60         if (settings.beforeSend.call(context, xhr, settings) === false ||
 61             triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
 62             return false
 63                 //觸發ajaxSend
 64         triggerGlobal(settings, context, 'ajaxSend', [xhr, settings])
 65     }
 66 
 67     function ajaxSuccess(data, xhr, settings, deferred) {
 68             var context = settings.context,
 69                 status = 'success'
 70                 //獲取上下文,調用success
 71             settings.success.call(context, data, status, xhr)
 72                 //若是引用了Deferred模塊,這裏執行成功的通知,並傳遞相關參數
 73             if (deferred) deferred.resolveWith(context, [data, status, xhr])
 74                 //觸發全局的成功
 75             triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
 76                 //調用ajaxComplete
 77             ajaxComplete(status, xhr, settings)
 78         }
 79         // type: "timeout", "error", "abort", "parsererror"
 80     function ajaxError(error, type, xhr, settings, deferred) {
 81             var context = settings.context
 82                 //獲取上下文,調用error
 83             settings.error.call(context, xhr, type, error)
 84                 //若是引用了Deferred模塊,這裏執行失敗的通知,並傳遞相關參數
 85             if (deferred) deferred.rejectWith(context, [xhr, type, error])
 86                 //觸發全局的失敗
 87             triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type])
 88                 //調用ajaxComplete
 89             ajaxComplete(type, xhr, settings)
 90         }
 91         // status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
 92     function ajaxComplete(status, xhr, settings) {
 93         var context = settings.context
 94             //調用setting裏面的complete
 95         settings.complete.call(context, xhr, status)
 96             //觸發ajaxComplete
 97         triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings])
 98             //調用ajaxStop
 99         ajaxStop(settings)
100     }
101 
102     // Empty function, used as default callback
103     function empty() {}
104 
105     $.ajaxJSONP = function(options, deferred) {
106             //沒有指定type
107             if (!('type' in options)) return $.ajax(options)
108 
109             var _callbackName = options.jsonpCallback,
110                 //調用options.jsonpCallback,獲得返回的字符串。
111                 //若是沒有任何返回就生成一個callbackName=jsonpXXX.
112                 callbackName = ($.isFunction(_callbackName) ?
113                     _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)),
114                 //建立一個script標籤,這裏貌似沒有jq的簡潔啊
115                 script = document.createElement('script'),
116                 //保存最初指定的window[callbackName] callback
117                 //若是調用options.jsonpCallback,但都沒返回這裏就是隨機生成的jsonpXXX函數名,window[callbackName]應該是不存在的
118                 originalCallback = window[callbackName],
119                 responseData,
120                 abort = function(errorType) {
121                     $(script).triggerHandler('error', errorType || 'abort')
122                 },
123                 xhr = {
124                     abort: abort
125                 },
126                 abortTimeout
127 
128             if (deferred) deferred.promise(xhr)
129 
130             $(script).on('load error', function(e, errorType) {
131                 clearTimeout(abortTimeout)
132                     //銷燬事件,移除元素
133                 $(script).off().remove()
134 
135                 if (e.type == 'error' || !responseData) {
136                     ajaxError(null, errorType || 'error', xhr, options, deferred)
137                 } else {
138                     ajaxSuccess(responseData[0], xhr, options, deferred)
139                 }
140                 //賦值回原來的callback
141                 window[callbackName] = originalCallback
142                     //最初保存的window[callbackName]
143                 if (responseData && $.isFunction(originalCallback))
144                 //調用
145                     originalCallback(responseData[0])
146                     //銷燬
147                 originalCallback = responseData = undefined
148             })
149 
150             if (ajaxBeforeSend(xhr, options) === false) {
151                 abort('abort')
152                 return xhr
153             }
154             //生成的jsonpXXX函數,指定callback=jsonpXXX。 請求成功返回,就會調用這個函數,給responseData賦值
155             //以前originalCallback = window[callbackName] 保存過了,這裏從新指定
156             window[callbackName] = function() {
157                 responseData = arguments
158             }
159             //處理簡寫 callback的狀況
160             script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName)
161                 //append到頁面上
162             document.head.appendChild(script)
163             //處理超時的狀況
164             if (options.timeout > 0) abortTimeout = setTimeout(function() {
165                 abort('timeout')
166             }, options.timeout)
167 
168             return xhr
169         }
170         //默認的setting
171     $.ajaxSettings = {
172         // Default type of request
173         type: 'GET',
174         // Callback that is executed before request
175         beforeSend: empty,
176         // Callback that is executed if the request succeeds
177         success: empty,
178         // Callback that is executed the the server drops error
179         error: empty,
180         // Callback that is executed on request complete (both: error and success)
181         complete: empty,
182         // The context for the callbacks
183         context: null,
184         // Whether to trigger "global" Ajax events
185         global: true,
186         // Transport
187         xhr: function() {
188             return new window.XMLHttpRequest()
189         },
190         // MIME types mapping
191         // IIS returns Javascript as "application/x-javascript"
192         accepts: {
193             script: 'text/javascript, application/javascript, application/x-javascript',
194             json: jsonType,
195             xml: 'application/xml, text/xml',
196             html: htmlType,
197             text: 'text/plain'
198         },
199         // Whether the request is to another domain
200         crossDomain: false,
201         // Default timeout
202         timeout: 0,
203         // Whether data should be serialized to string
204         processData: true,
205         // Whether the browser should be allowed to cache GET responses
206         cache: true
207     }
208 
209     function mimeToDataType(mime) {
210         if (mime) mime = mime.split(';', 2)[0]
211         return mime && (mime == htmlType ? 'html' :
212             mime == jsonType ? 'json' :
213             scriptTypeRE.test(mime) ? 'script' :
214             xmlTypeRE.test(mime) && 'xml') || 'text'
215     }
216 
217     //參數拼接
218     function appendQuery(url, query) {
219         if (query == '') return url
220         return (url + '&' + query).replace(/[&?]{1,2}/, '?')
221     }
222 
223     // serialize payload and append it to the URL for GET requests
224     function serializeData(options) {
225         //要處理data,data存在、不是string類型
226         if (options.processData && options.data && $.type(options.data) != "string")
227             options.data = $.param(options.data, options.traditional)
228         if (options.data && (!options.type || options.type.toUpperCase() == 'GET'))
229             options.url = appendQuery(options.url, options.data), options.data = undefined
230     }
231 
232     $.ajax = function(options) {
233         //處理外出傳入的options,若是加載了Deferred 模塊,這裏建立一個Deferred實例對象
234         var settings = $.extend({}, options || {}),
235             deferred = $.Deferred && $.Deferred(),
236             urlAnchor
237             //把settings中沒有$.ajaxSettings中的設置 都給賦值到settings裏面
238         for (key in $.ajaxSettings)
239             if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]
240 
241             //調用ajaxStart
242         ajaxStart(settings)
243 
244         //若是不是跨域
245         if (!settings.crossDomain) {
246             urlAnchor = document.createElement('a')
247             urlAnchor.href = settings.url
248             urlAnchor.href = urlAnchor.href
249                 //內部判斷當前的url是否跨域
250             settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host)
251         }
252         //沒有設置url  默認爲當前頁面的地址
253         if (!settings.url) settings.url = window.location.toString()
254 
255         //處理裏面的data
256         serializeData(settings)
257 
258         var dataType = settings.dataType,
259             //若是請求的是jsonp,則將地址欄裏的=?替換爲callback=?,至關於一個簡寫
260             hasPlaceholder = /\?.+=\?/.test(settings.url)
261         if (hasPlaceholder) dataType = 'jsonp'
262 
263         if (settings.cache === false || (
264                 (!options || options.cache !== true) &&
265                 ('script' == dataType || 'jsonp' == dataType)
266             ))
267         //不緩存,在url後面添加時間戳
268             settings.url = appendQuery(settings.url, '_=' + Date.now())
269 
270         //針對jsonp進行處理
271         if ('jsonp' == dataType) {
272             if (!hasPlaceholder)
273                 settings.url = appendQuery(settings.url, settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?')
274             return $.ajaxJSONP(settings, deferred)
275         }
276 
277         var mime = settings.accepts[dataType],
278             headers = {},
279             setHeader = function(name, value) {
280                 headers[name.toLowerCase()] = [name, value]
281             },
282             protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
283             xhr = settings.xhr(),
284             nativeSetHeader = xhr.setRequestHeader,
285             abortTimeout
286             //引入了Deferred沒款,這裏xhr就附加了promise對象的相關行爲,最後返回的是一個promise對象,後續就能夠鏈式done
287         if (deferred) deferred.promise(xhr)
288             //設置header
289         if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest')
290         setHeader('Accept', mime || '*/*')
291 
292         if (mime = settings.mimeType || mime) {
293             if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0]
294             xhr.overrideMimeType && xhr.overrideMimeType(mime)
295         }
296 
297         //設置contentType
298         if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET'))
299             setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded')
300 
301         if (settings.headers)
302             for (name in settings.headers) setHeader(name, settings.headers[name])
303 
304         xhr.setRequestHeader = setHeader
305 
306         xhr.onreadystatechange = function() {
307             if (xhr.readyState == 4) {
308                 xhr.onreadystatechange = empty
309                     //清楚setTimeout
310                 clearTimeout(abortTimeout)
311                 var result, error = false
312                     //根據狀態來判斷請求是否成功
313                     //狀態>=200 && < 300 表示成功
314                     //狀態 == 304 表示文件未改動過,也可認爲成功
315                     //若是是取要本地文件那也能夠認爲是成功的,xhr.status == 0是在直接打開頁面時發生請求時出現的狀態,也就是否是用localhost的形式訪問的頁面的狀況
316                 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
317                     //取到dataType
318                     dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'))
319                         //取到返回的數據
320                     result = xhr.responseText
321 
322                     try {
323                         // http://perfectionkills.com/global-eval-what-are-the-options/
324                         if (dataType == 'script')(1, eval)(result)
325                         else if (dataType == 'xml') result = xhr.responseXML
326                         else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
327                     } catch (e) {
328                         error = e
329                     }
330                     //調用ajaxError
331                     if (error) ajaxError(error, 'parsererror', xhr, settings, deferred)
332                     else ajaxSuccess(result, xhr, settings, deferred)
333                 } else {
334                     ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred)
335                 }
336             }
337         }
338 
339         //ajaxBeforeSend 返回false
340         if (ajaxBeforeSend(xhr, settings) === false) {
341             xhr.abort()
342             ajaxError(null, 'abort', xhr, settings, deferred)
343             return xhr
344         }
345         //設置請求頭信息
346         if (settings.xhrFields)
347             for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name]
348 
349         var async = 'async' in settings ? settings.async : true
350             //發起請求
351         xhr.open(settings.type, settings.url, async, settings.username, settings.password)
352 
353         for (name in headers) nativeSetHeader.apply(xhr, headers[name])
354 
355         //當設置了settings.timeout,則在超時後取消請求,並執行timeout事件處理函數,並處罰error
356         if (settings.timeout > 0) abortTimeout = setTimeout(function() {
357             xhr.onreadystatechange = empty
358             xhr.abort()
359             ajaxError(null, 'timeout', xhr, settings, deferred)
360         }, settings.timeout)
361 
362         // avoid sending empty string (#319)
363         xhr.send(settings.data ? settings.data : null)
364             //若是引入了Deferred模塊這裏返回promise對象
365         return xhr
366     }
367 
368     //處理多個參數組合的狀況
369     // handle optional data/success arguments
370     function parseArguments(url, data, success, dataType) {
371         if ($.isFunction(data)) dataType = success, success = data, data = undefined
372         if (!$.isFunction(success)) dataType = success, success = undefined
373         return {
374             url: url,
375             data: data,
376             success: success,
377             dataType: dataType
378         }
379     }
380 
381     $.get = function( /* url, data, success, dataType */ ) {
382         return $.ajax(parseArguments.apply(null, arguments))
383     }
384 
385     $.post = function( /* url, data, success, dataType */ ) {
386         var options = parseArguments.apply(null, arguments)
387         options.type = 'POST'
388         return $.ajax(options)
389     }
390 
391     $.getJSON = function( /* url, data, success */ ) {
392         var options = parseArguments.apply(null, arguments)
393         options.dataType = 'json'
394         return $.ajax(options)
395     }
396 
397     $.fn.load = function(url, data, success) {
398         if (!this.length) return this
399         var self = this,
400             parts = url.split(/\s/),
401             selector,
402             options = parseArguments(url, data, success),
403             callback = options.success
404         if (parts.length > 1) options.url = parts[0], selector = parts[1]
405         options.success = function(response) {
406             //建立一個div,把返回的字符串script替換掉,把放字符串入div中,經過選擇器找到對應元素
407             self.html(selector ?
408                     $('<div>').html(response.replace(rscript, "")).find(selector) : response)
409                 //調用傳入的回調函數
410             callback && callback.apply(self, arguments)
411         }
412         $.ajax(options)
413         return this
414     }
415 
416     var escape = encodeURIComponent
417 
418     function serialize(params, obj, traditional, scope) {
419         var type, array = $.isArray(obj),
420             hash = $.isPlainObject(obj)
421         $.each(obj, function(key, value) {
422             type = $.type(value)
423                 //scope用做處理value也是object或者array的狀況
424                 //traditional表示是否以傳統的方式拼接數據,
425                 //傳統的意思就是好比現有一個數據{a:[1,2,3]},轉成查詢字符串後結果爲'a=1&a=2&a=3'
426                 //非傳統的的結果則是a[]=1&a[]=2&a[]=3
427             if (scope) key = traditional ? scope :
428                 scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']'
429                 // handle data in serializeArray() format
430                 //當處理的數據爲[{},{},{}]這種狀況的時候,通常指的是序列化表單後的結果
431             if (!scope && array) params.add(value.name, value.value)
432                 // recurse into nested objects
433                 // 當value值是數組或者是對象且不是按傳統的方式序列化的時候,須要再次遍歷value
434             else if (type == "array" || (!traditional && type == "object"))
435                 serialize(params, value, traditional, key)
436             else params.add(key, value)
437         })
438     }
439 
440     $.param = function(obj, traditional) {
441         var params = []
442         params.add = function(key, value) {
443             if ($.isFunction(value)) value = value()
444             if (value == null) value = ""
445             this.push(escape(key) + '=' + escape(value))
446         }
447         serialize(params, obj, traditional)
448         return params.join('&').replace(/%20/g, '+')
449     }
450 })(Zepto); 
zepto ajax

 本文地址:http://www.cnblogs.com/Bond/p/4210808.html 

相關文章
相關標籤/搜索