zoukankan      html  css  js  c++  java
  • zepto源码学习-05 ajax

    学习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[^<]*(?:(?!</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 

     

  • 相关阅读:
    一些对数学领域及数学研究的个人看法(转载自博士论坛wcboy)
    dynamic与var
    转载-V.I.Arnold, beyond a mathematician
    转载---青年问禅师
    转载-傅里叶级数的几何意义 – 巧妙记忆公式的方法
    转载--柯尔莫哥洛夫
    转载--黎曼
    动态规划复习
    几匹单调队列水题
    fastIO
  • 原文地址:https://www.cnblogs.com/Bond/p/4210808.html
Copyright © 2011-2022 走看看