學習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>
點擊按鈕,控制檯輸出以下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)的形勢返回。
此時頁面控制檯輸入以下
$.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) } } }
內部處理就是建立一個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);