学习zeptoajax之前需要先脑补下,强烈推荐此文http://www.cnblogs.com/heyuquan/archive/2013/05/13/js-jquery-ajax.html
还有Aaron 的jquery源码分析系列,讲jquery的ajax的部分,当然其他也可以看,很值得学习。
zepto ajax部分的设计相对简单,毕竟zepto就是轻量级的库,没有jqeury那样复杂,jquery ajax是依赖于Deferred模块的,整个代码一千多行。而zepto只有几百行,整体设计相对简单,ajax部分可以不依赖于Deferred模块,独立运行。zepto ajax的使用相见 地址,先看懂使用然后再分析具体实现。
先看一个demo
<!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
点击按钮,控制台输出如下
以上图片基本上展示了正常情况下zepto ajax的处理流程,测试代码我是引入了Deferred模块的,所以可以链式调用done(后续会说)。
XHR那行输出是我默认开启chrome的Log XMLHttpRequests,所有的XMLHttpRequests都会在控制台输出。
再看一个发生错误的输出,一旦请求发生错误,后续的处理流程将发生变化。
再看跨域时候的情况,这个测试我指定了jsonpCallback为log2,dataType为jsonp,地址为http://192.168.11.198:8080/myTest/test/ajax-page.php。ajax请求部分参数如下
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代码如下
<?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有五种状态:、01、2、3、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