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函數需要在分發器中腳本加載后才能執行,從而才能截取到服務器返回的數據

 

我也不可能每個都分析到位,所以大家有選擇的自己根據需求去看源碼吧,大體的流程思路理解的,看起來就很快了,至於其余的類型,在之后遇到了就會在分析了


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: