• jQuery源码分析系列(37) : Ajax 总结


    综合前面的分析,我们总结如下3大块:

    • jQuery1.5以后,AJAX模块提供了三个新的方法用于管理、扩展AJAX请求
      1. 前置过滤器 jQuery. ajaxPrefilter
      2. 请求分发器 jQuery. ajaxTransport
      3. 类型转换器 ajaxConvert
    • 为了整体性与扩展性考虑,把整个结构通过Deferred实现异步链式模型,Promise对象可以轻易的绑定成功、失败、进行中三种状态的回调函数,然后通过在状态码在来回调不同的函数就行了
    • 出于同源策略考虑,存在跨域问题,所以ajax内部的处理总的来分2大块
      1. 基于XMLHttpRequest的ajax请求
      2. 基于script的jsonp跨域请求

    引入Deferred统一回调体系

    jQuery的链式方法是大是通过返回this的引用,但是ajax的链式不是那么简单的,因为ajax可以异步操作,所以返回的是一个异步模型对象Promise

    当然如果只是deferred = jQuery.Deferred() 返回这个对象也是没意义的,因为无法关联到实际的数据

    所以jquery内部构建了一个增强版的jqXHR对象,除了混入Promise模型,还增强了一些方法与接口

    jqXHR 扩充基本的方法与接口

    jqXHR = {
    
        // 准备状态
        readyState: 0,
    
        // Builds headers hashtable if needed
        // 如果需要,创建一个响应头参数的表
        getResponseHeader: function(key) {
            var match;
            // 如果状态为2,状态2表示ajax完成
            if (state === 2) {
                // 如果没有相应头
                if (!responseHeaders) {
                    // 相应头设空
                    responseHeaders = {};
                    // 组装相应头
                    while ((match = rheaders.exec(responseHeadersString))) {
                        responseHeaders[match[1].toLowerCase()] = match[2];
                    }
                }
                // 响应头对应的key的值
                match = responseHeaders[key.toLowerCase()];
            }
            return match == null ? null : match;
        },
    
        // Raw string
        // 返回响应头字符串
        getAllResponseHeaders: function() {
            // 看看是否接收到了,接收到直接返回,否则为null
            return state === 2 ? responseHeadersString : null;
        },
    
        // Caches the header
        // 设置请求头
        setRequestHeader: function(name, value) {
            var lname = name.toLowerCase();
            if (!state) {
                // 如果requestHeadersNames[ lname ]不为空,
                // 则requestHeadersNames[ lname ]不变,name设置为该值
                // 否则,requestHeadersNames[ lname ]不空,
                // 则requestHeadersNames[ lname ]设置为name,
                // 该映射关系用于避免用户大小写书写错误之类的问题,容错处理
                name = requestHeadersNames[lname] = requestHeadersNames[lname] || name;
    
                //现在的name是对的,或者是第一次设置这个name,不需要容错
                //设置请求头对应值
                requestHeaders[name] = value;
            }
            return this;
        },
    
        // Overrides response content-type header
        // 重写相应头content-type
        overrideMimeType: function(type) {
            if (!state) {
                s.mimeType = type;
            }
            return this;
        },
    
        // Status-dependent callbacks
        // 对应状态的回调函数集
        statusCode: function(map) {
            var code;
            if (map) {
                //如果状态小于2,表示旧的回调可能还没有用到
                if (state < 2) {
                    for (code in map) {
                        // Lazy-add the new callback in a way that preserves old ones
                        //  用类似链表的方式添加,以保证旧的回调依然存在
                        statusCode[code] = [statusCode[code], map[code]];
                    }
                } else {
                    // Execute the appropriate callbacks
                    // 无论Deferred成功还是失败都执行当前状态回调
                    jqXHR.always(map[jqXHR.status]);
                }
            }
            return this
        },
    
        // Cancel the request
        abort: function(statusText) {
            var finalText = statusText || strAbort;
            if (transport) {
                transport.abort(finalText);
            }
            done(0, finalText);
            return this;
        }
    };

    看看我们ajax的写法

    $.ajax({
        url: "php.html",
        context: document.body,
        complete: function() {
            console.log(this)
        }
    }).done(function() {
        console.log(this)
    });

    链式了一个done方法,done是Promise模型中的成功回调,因为ajax返回的是jqXHR对象

    所以jqXHR就需要混入Promise模型

    deferred.promise(jqXHR).complete = completeDeferred.add;
     jqXHR.success = jqXHR.done;
     jqXHR.error   = jqXHR.fail;

    看ajax源码前需要了解回到队列与Deferreds

    jqXHR混入了Promise模型处理,当然只能读防止修改,之外还增加了一个complete的的回调队列

    image_thumb2

    具体怎么使用,看后面

    for (i in {
        success  : 1,
        error    : 1,
        complete : 1
    }) {
        jqXHR[i](s[i]);
    }

    很巧妙的一个处理,把用户配置文件中的回调函数给注册到这个jqXHR的回调体系中

    所以就把所有的有关回调都绑定到了jqXHR对象上了


    ajax提供3种事件通知接口

    1 提供全局事件,外部的视图可以根据ajax状态进行改变

    2 提供内部事件接口,根据流程做相对应的处理

    3 提供链式事件接口,通过Promise实现

    当ajax开始时模拟全局事件,ajaxStart

    这里主要利用了jQuery.event.trigger和jQuery.fn.trigger模拟发事件

    if (fireGlobals && jQuery.active++ === 0) {
         // 则通过jQuery.event.trigger模拟触发
        jQuery.event.trigger("ajaxStart");
    }

    ajax发送消息,触发ajaxSend

    /**
     * 全局事件ajaxSend
     * 如果需要,对特定对象触发全局事件ajaxSend
     */
    if (fireGlobals) {
        globalEventContext.trigger("ajaxSend", [jqXHR, s]);
    }

    结束时触发ajaxSuccess或ajaxError,再出发ajaxComplete,如果全部ajax结束则触发ajaxStop。

    if (fireGlobals) {
                    globalEventContext.trigger(isSuccess ? "ajaxSuccess" : "ajaxError", [jqXHR, s, isSuccess ? success : error]);
                }
    
                // Complete
                completeDeferred.fireWith(callbackContext, [jqXHR, statusText]);
    
                if (fireGlobals) {
                    globalEventContext.trigger("ajaxComplete", [jqXHR, s]);
                    // Handle the global AJAX counter
                    if (!(--jQuery.active)) {
                        jQuery.event.trigger("ajaxStop");
                    }
                }

    关于缓存数据

    如果我们的请求为GET的时候的处理,用户通过data自定义了一些数据,那么这些数据只能通过拼接成url传递给服务端

    我们需要通过给地址附加参数_=xxx来避免缓存

    if (s.cache === false) {
        s.url = rts.test(cacheURL) ?
        // If there is already a '_' parameter, set its value
        cacheURL.replace(rts, "$1_=" + ajax_nonce++) :
        // Otherwise add one to the end
        cacheURL + (ajax_rquery.test(cacheURL) ? "&" : "?") + "_=" + ajax_nonce++;
    }

    从jQuery 1.5开始$.ajax() 返回XMLHttpRequest(jqXHR)对象,该对象是浏览器的原生的XMLHttpRequest对象的一个超集。

    例如,它包含responseTextresponseXML属性,以及一个getResponseHeader()方法。

    当传输机制不是是XMLHttpRequest时(例如,一个JSONP请求脚本,返回一个脚本 tag 时),jqXHR对象尽可能的模拟原生的XHR功能。

    从jQuery 1.5.1开始jqXHR对象还包含了overrideMimeType方法 (它在jQuery 1.4.x中是有效的,但是在jQuery 1.5中暂时的被移除)。

    .overrideMimeType() 方法可能用在beforeSend()的回调函数中,

    例如,修改响应的Content-Type信息头:

    $.ajax({
      url: "http://fiddle.jshell.net/favicon.png",
      beforeSend: function ( xhr ) {
        xhr.overrideMimeType("text/plain; charset=x-user-defined");
      }
    }).done(function ( data ) {
      if( console && console.log ) {
        console.log("Sample of data:", data.slice(0, 100));
      }
    });

    从 jQuery 1.5 开始,$.ajax()返回的jqXHR对象 实现了 Promise 接口, 使它拥有了 Promise 的所有属性,方法和行为。(见Deferred object获取更多信息)。

    为了让回调函数的名字统一,便于在$.ajax()中使用。jqXHR也提供.error() .success().complete()方法。这些方法都带有一个参数,该参数是一个函数,此函数在 $.ajax()请求结束时被调用,并且这个函数接收的参数,与调用 $.ajax()函数时的参数是一致。这将允许你在一次请求时,对多个回调函数进行赋值,甚至允许你在请求已经完成后,对回调函数进行赋值(如果该请求已经完成,则回调函数会被立刻调用)。

    更多的更多大家还是仔细参考API吧

  • 相关阅读:
    急招.NET系列职位
    程序员成长的三个方法
    xwebkitspeech
    张小龙的产品
    浅析商业银行“业务连续性管理体系”的构建
    Sonar for dotNet
    Moles测试Contrustor时候遇到的一个问题
    EntityFramework 用Moles的mock
    Accessor中Generic的元素是internal/private的会导致转换失败的异常
    Android自用Intent 介绍
  • 原文地址:https://www.cnblogs.com/aaronjs/p/3798868.html
Copyright © 2020-2023  润新知