jQuery源码分析系列(36) : Ajax - 类型转化器

什么是类型转化器?

jQuery支持不同格式的数据返回形式,比如dataType为 xml, json,jsonp,script, or html

但是浏览器的XMLHttpRequest对象对数据的响应只有 responseText与responseXML 二种

所以现在我要定义dataType为jsonp,那么所得的最终数据是一个json的键值对,所以jQuery内部就会默认帮你完成这个转化工作

jQuery为了处理这种执行后数据的转化,就引入了类型转化器,如果没有指定类型就依据响应头Content-Type自动处理

数据传输,服务器只能返回字符串形式的,所以如果我们dataType为jsop或者json的时候

服务器返回的数据为

responseText: "{"a":1,"b":2,"c":3,"d":4,"e":5}"
给转化成
responseJSON: Object
    {
        a: 1
        b: 2
        c: 3
        d: 4
        e: 5
    }


服务器的传输返回的只能是string类型的数据,但是用户如果通过jQuery的dataType定义了json的格式后,会默认把数据转换成Object的形式返回

这就是jQuery内部做的智能处理了

jQuery内把自定义的dataType与服务器返回的数据做相对应的映射处理,通过converters存储对应的处理句柄

把需要类型转换器ajaxConvert在服务端响应成功后,对定义在jQuery. ajaxSettings中的converters进行遍历,找到与数据类型相匹配的转换函数,并执行。

converters的映射:

converters: {
           // Convert anything to text、
           // 任意内容转换为字符串
           // window.String 将会在min文件中被压缩为 a.String
           "* text": window.String,

           // Text to html (true = no transformation)
           // 文本转换为HTML(true表示不需要转换,直接返回)
           "text html": true,

           // Evaluate text as a json expression
           // 文本转换为JSON
           "text json": jQuery.parseJSON,

           // Parse text as xml
           // 文本转换为XML
           "text xml": jQuery.parseXML
       }

除此之外还有额外扩展的一部分jsonp的处理

// Ajax请求设置默认的值
jQuery.ajaxSetup({
    /**
     * 内容类型发送请求头(Content-Type),用于通知服务器该请求需要接收何种类型的返回结果。
     * 如果accepts设置需要修改,推荐在$.ajaxSetup() 方法中设置一次。
     * @type {Object}
     */
    accepts: {
        script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
    },
    contents: {
        script: /(?:java|ecma)script/
    },
    converters: {
        "text script": function(text) {
            jQuery.globalEval(text);
            return text;
        }
    }
});

所以其格式就是

text –> (html,json,script)的处理了

其寓意就是服务器返回的用于只是string类型的文本格式,需要转化成用户想要的dataType类型的数据

{"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML}



类型的转化都是发生在服务器返回数据后,所以对应的就是ajax 方法中的done之后,当然这个done方法也是经过请求分发器包装过的,至于为什么要这样处理上章就已经提过到了,为了处理正常请求与jsonp的跨域请求的问题

所以当AJAX请求完成后,会调用闭包函数done,在done中判断本次请求是否成功,如果成功就调用ajaxConvert对响应的数据进行类型转换

所以在此之前需要:

1:正确分配dataType类型,如果用户不设置(空)的情况

2:需要转化成converters映射表对应的格式比如(* text, text html , text xml , text json)



dataType类型的转化

dataType类型的参数,可以是xml, json, script, or html 或者干脆为空,那么jQuery就需要一个只能的方法去判断当前是属于什么数据处理

因此就引入了

ajaxHandleResponses 处理响应转化器,解析出正确的dataType类型
response = ajaxHandleResponses(s, jqXHR, responses);

dataType无法就那么几种情况

1:dataType为空,自动转化

此时jQuery只能根据头部信息是猜测当前需要处理的类型

// 删除掉通配dataType,得到返回的Content-Type
while (dataTypes[0] === "*") {
    dataTypes.shift();
    if (ct === undefined) {
        ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
    }
}

通过xhr.getAllResponseHeaders()得到头部信息,然后去匹配Content-Type所有对象的值即可

当然找到这个Content-Type = “html”,我们还得看看有没有对应处理的方法,如果有就需要替换这个dataTypes

// 看看是不是我们能处理的Content-Type,比如图片这类二进制类型就不好处理了
if (ct) {
    // 实际上能处理的就是text、xml和json
    for (type in contents) {
        if (contents[type] && contents[type].test(ct)) {
            dataTypes.unshift(type);
            break;
        }
    }
}

经过这个流程后,dataTypes 本来是* 就变成了对应的html了,这是jquery内部的自动转化过程



2:dataType开发者指定

xml, json, script, html, jsop

总结:

类型转换器将服务端响应的responseText或responseXML,转换为请求时指定的数据类型dataType,

如果没有指定类型就依据响应头Content-Type自动处理


类型转换器的执行过程
response = ajaxConvert(s, response, jqXHR, isSuccess);

源码部分

function ajaxConvert(s, response, jqXHR, isSuccess) {
    current = dataTypes.shift();
    while (current) {
        if (current) {
            // 如果碰到了*号,即一个任意类型,而转换为任意类型*没有意义
            if (current === "*") {
                current = prev;
                // 转化的重点
                // 如果不是任意的类型,并且找到了一个不同的类型
            } else if (prev !== "*" && prev !== current) {

                // Seek a direct converter
                // 组成映射格式,匹配转化器
                // * text: function String() { [native code] }
                // script json: function () {
                // text html: true
                // text json: function parse() { [native code] }
                // text script: function (text) {
                // text xml: function (data) {
                conv = converters[prev + " " + current] || converters["* " + current];

                // If none found, seek a pair
                // 假如找不到转化器
                // jsonp是有浏览器执行的呢,还是要调用globalEval
                if (!conv) {
                    //...............
                }

                // Apply converter (if not an equivalence)
                // 如果有对应的处理句柄,执行转化
                if (conv !== true) {

                    // Unless errors are allowed to bubble, catch and return them
                    if (conv && s["throws"]) {
                        response = conv(response);
                    } else {
                        try {
                            //执行对应的处理句柄,传入服务器返回的数据
                            response = conv(response);
                        } catch (e) {
                            return {
                                state: "parsererror",
                                error: conv ? e : "No conversion from " + prev + " to " + current
                            };
                        }
                    }
                }
            }
        }
    }

    return {
        state: "success",
        data: response
    };
}

流程

1.遍历dataTypes中对应的处理规则【"script","json"】

2.制作jqXHR对象的返回数据接口

  1. json: "responseJSON"
  2. text: "responseText"
  3. xml: "responseXML"

如:jqXHR.responseText: "{"a":1,"b":2,"c":3,"d":4,"e":5}"

3. 生成转化器对应的匹配规则,寻找合适的处理器

4. 返回处理后的数据response



分析一下特殊的jsonp的转化流程

先看看转化对应的处理器

jsonp:

converters["script json"] = function() {
            if (!responseContainer) {
                jQuery.error(callbackName + " was not called");
            }
            return responseContainer[0];
};

jsonp的转化器只是很简单的从responseContainer取出了对应的值,所以responseContainer肯定在转化之后就应该把数据给转化成数组对象了

当然做源码分析需要一点自己想猜想能力,比如

responseContainer这个数组对象如何而来?

那么我们知道jsonp的处理的原理,还是通过加载script,然后服务器返回一个回调函数,responseContainer数据就是回调函数的实参

所以需要满足responseContainer的处理,必须要先满足脚本先加载,所以我们要去分发器中找对应的加载代码

首先responseContainer是内部变量,只有一个来源处,在预处理的时候增加一个全局的临时函数

然后代码肯定是执行了这个函数才能把arguments参数赋给responseContainer

overwritten = window[callbackName];
window[callbackName] = function() {
    responseContainer = arguments;
};

callbcakName是内部创建的一个尼玛函数名

jQuery203029543792246840894_1403062512436 = function() {
    responseContainer = arguments;
};

我们发送请求

http://192.168.1.114/yii/demos/test.php?backfunc=jQuery203029543792246840894_1403062512436&action=aaron&_=1403062601515

服务器那边就回调后,执行了jQuery203029543792246840894_1403062512436(responseContainer );

所以全局的callbackName函数需要在分发器中脚本加载后才能执行,从而才能截取到服务器返回的数据

我也不可能每个都分析到位,所以大家有选择的自己根据需求去看源码吧,大体的流程思路理解的,看起来就很快了,至于其余的类型,在之后遇到了就会在分析了

jQuery源码分析系列(36) : Ajax - 类型转化器,布布扣,bubuko.com

时间: 2024-10-11 12:34:31

jQuery源码分析系列(36) : Ajax - 类型转化器的相关文章

jQuery源码分析系列(33) : AJAX中的前置过滤器和请求分发器

jQuery1.5以后,AJAX模块提供了三个新的方法用于管理.扩展AJAX请求,分别是: 1.前置过滤器 jQuery. ajaxPrefilter 2.请求分发器 jQuery. ajaxTransport, 3.类型转换器 ajaxConvert 源码结构: jQuery.extend({ /** * 前置过滤器 * @type {[type]} */ ajaxPrefilter: addToPrefiltersOrTransports(prefilters), /** * 请求分发器 *

jQuery源码分析系列(34) : Ajax - 预处理jsonp

上一章大概讲了前置过滤器和请求分发器的作用,这一章主要是具体分析每种对应的处理方式 $.ajax()调用不同类型的响应,被传递到成功处理函数之前,会经过不同种类的预处理(prefilters). 预处理的类型取决于由更加接近默认的Content-Type响应,但可以明确使用dataType选项进行设置.如果提供了dataType选项, 响应的Content-Type头信息将被忽略. 有效的数据类型是text, html, xml, json,jsonp,和 script. dataType:预期

jQuery源码分析系列(35) : Ajax - jsonp的实现与原理

ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本 json核心就是:允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了. jquery ext dojo这类库的实现手段其实大同小异 在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但img.iframe.s

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

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

jQuery源码分析系列(31) : Ajax deferred实现 - Aaron.

AJAX的底层实现都是浏览器提供的,所以任何基于api上面的框架或者库,都只是说对于功能的灵活与兼容维护性做出最优的扩展 ajax请求的流程: 1.通过 new XMLHttpRequest 或其它的形式(指IE)生成ajax的对象xhr. 2.通过xhr.open(type, url, async, username, password)的形式建立一个连接. 3.通过setRequestHeader设定xhr的请求头部(request header). 4.通过send(data)请求服务器端

jQuery源码分析系列(31) : Ajax deferred实现

AJAX的底层实现都是浏览器提供的,所以任何基于api上面的框架或者库,都只是说对于功能的灵活与兼容维护性做出最优的扩展 ajax请求的流程: 1.通过 new XMLHttpRequest 或其它的形式(指IE)生成ajax的对象xhr. 2.通过xhr.open(type, url, async, username, password)的形式建立一个连接. 3.通过setRequestHeader设定xhr的请求头部(request header). 4.通过send(data)请求服务器端

[转]jQuery源码分析系列

文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaron/jQuery 正在编写的书 - jQuery架构设计与实现 本人在慕课网的教程(完结) jQuery源码解析(架构与依赖模块) 64课时 jQuery源码解析(DOM与核心模块)64课时 jQuery源码分析目录(完结) jQuery源码分析系列(01) : 整体架构 jQuery源码分析系列(

jQuery源码分析系列(38) : 队列操作

Queue队列,如同data数据缓存与Deferred异步模型一样,都是jQuery库的内部实现的基础设施 Queue队列是animate动画依赖的基础设施,整个jQuery中队列仅供给动画使用 Queue队列 队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除. 为什么要引入队列? 我们知道代码的执行流有异步与同步之分,例如 var a

jQuery源码分析系列(39) : 动画队列

data函数在jQuery中只有短短的300行代码,非常不起点 ,剖析源码的时候你会发现jQuery只要在有需要保存数据的地方无时无刻不依赖这个基础设施 动画会调用队列,队列会调用data数据接口还保存队列里面的的动画数据 所以我们在自习回顾下关于数据缓存 //These may be used throughout the jQuery core codebase //存数据的 //用户使用 data_user = new Data(); //存储对象 //jQuery内部私有 //用来存事件