jQuery源碼分析系列(34) : Ajax - 預處理jsonp

上一章大概講了前置過濾器和請求分發器的做用,這一章主要是具體分析每種對應的處理方式javascript

$.ajax()調用不一樣類型的響應,被傳遞到成功處理函數以前,會通過不一樣種類的預處理(prefilters)。 預處理的類型取決於由更加接近默認的Content-Type響應,但能夠明確使用dataType選項進行設置。若是提供了dataType選項, 響應的Content-Type頭信息將被忽略。css

有效的數據類型是text, html, xml, json,jsonp,和 script.html

dataType:預期服務器返回的數據類型。若是不指定,jQuery 將自動根據 HTTP 包 MIME 信息來智能判斷,好比XML MIME類型就被識別爲XML。在1.4中,JSON就會生成一個JavaScript對象,而script則會執行這個腳本。隨後服務器端返回的數據會根據這個值解析後,傳遞給回調函數。可用值:java

 


script 類型jquery

$.ajax({
    type     : "GET",
    url      : "test.js",
    dataType : "script",
    complete: function(jqXHR, status) {
        console.log(jqXHR, status)
    }
});

根據API的說明可知,若是dataType類型爲script的時候,須要處理ajax

1 執行腳本json

2 內容看成純文本返回跨域

3 默認狀況下不會經過在URL中附加查詢字符串變量 "_=[TIMESTAMP]" 進行自動緩存結果,除非設置了cache參數爲true瀏覽器

4 在遠程請求時(不在同一個域下),全部POST請求都將轉爲GET請求。(由於將使用DOM的script標籤來加載)緩存

針對上述四點,咱們看看處理的流程

inspectPrefiltersOrTransports(prefilters, s, options, jqXHR);

此時的dataType類型就會通過對應的預處理ajaxPrefilter("script")

cache (默認: true, dataType爲"script"和"jsonp"時默認爲false)

jQuery.ajaxPrefilter("script", function(s) {
    if (s.cache === undefined) {
        s.cache = false;
    }
    if (s.crossDomain) {
        s.type = "GET";
    }
});

預處理的處理就是將其緩存爲設置爲 false ,瀏覽器將不緩存此頁面

這將在請求的URL的查詢字符串中追加一個時間戳參數,以確保每次瀏覽器下載的腳本被從新請求

工做原理是在GET請求參數中附加"_={timestamp}"在請求的地址後面加一個時間戳

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++;
}

此時的 s.url = "test.js?_=1402362401890"; 

該參數不是其餘請求所必須的,除了在IE8中,當一個POST請求一個已經用GET請求過的URL

 


json jsonp 類型

  • "json":  把響應的結果看成 JSON 執行,並返回一個JavaScript對象。在 jQuery 1.4 中,JSON 格式的數據以嚴格的方式解析,若是格式有錯誤,jQuery都會被拒絕並拋出一個解析錯誤的異常。(見json.org的更多信息,正確的JSON格式。)
  • 若是指定的是json,響應結果做爲一個對象,在傳遞給成功處理函數以前使用jQuery.parseJSON進行解析。 解析後的JSON對象能夠經過該jqXHR對象的responseJSON屬性得到的。
  • json的處理只要是在ajaxConvert方法中把結果給轉換成須要是json格式,這是後面的內容,這裏主要研究下jsonp的預處理

關於JSONP傳送飛機:http://baike.baidu.com/view/2131174.htm

JSONP是一個非官方的協議,它容許在服務器端集成Script tags返回至客戶端,經過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。JSON系統開發方法是一種典型的面向數據結構的分析和設計方法,以活動爲中心,一連串的活動的順序組合成一個完整的工做進程。

跨域這個問題的產生根本緣由是瀏覽器的同源策略限制,理解同源策略的限制同源策略是指阻止代碼得到或者更改從另外一個域名下得到的文件或者信息。也就是說咱們的請求地址必須和當前網站的地指相同。同源策略經過隔離來實現對資源的保護。這個策略的歷史很是悠久從Netscape Navigator 2.0時代就開始了。

  • 解決這個限制的一個相對簡單的辦法就是在服務器端發送請求,服務器充當一個到達第三方資源的代理中繼。雖然是用普遍可是這個方法卻不夠靈活。
  • 另外一個辦法就是使用框架(frames),將第三方站點的資源包含進來,可是包含進來的資源一樣要受到同源策略的限制。
  • 有一個很巧妙的辦法就是在頁面中使用動態代碼元素,代碼的源指向服務地址並在本身的代碼中加載數據。當這些代碼加載執行的時候,同源策略就不會起到限制。可是若是代碼試圖下載文件的時候執行仍是會失敗,幸運的是,咱們可使用JSON(JavaScript Object Notation)來改進這個應用

JSON和JSONP

與XML相比,JSON是一個輕量級的數據交換格式。JSON對於JavaScript開發人員充滿魅力的緣由在於JSON自己就是Javascript中的對象。

例如一個ticker對象

var ticker = {symbol:'IBM',price:100}

而JSON串就是 {symbol:'IBM',price:100}

這樣咱們就能夠在函數的參數中傳遞JSON數據。咱們很容易掌握在函數中使用動態的JSON參數數據,可是咱們的目的並非這個。

經過使咱們的函數可以加載動態的JSON數據,咱們就可以處理動態的數據,這項技術叫作 Dynamic Javascript Insertion。

index.html 中

function showPrice(data){  
    alert("Symbol:" + data.symbol + ", Price:" + data.price);  
}

而後動態加載ticker.js腳本

var data = {symbol:'IBM', price:100};  
showPrice(data);

代碼經過動態加入Javascript代碼,來執行函數加載數據

正如以前提到過的,同源策略對於動態插入的代碼不適用。也就是你能夠從不一樣的域中加載代碼,來執行在他們代碼中的JSON數據。

這就是JSONP(JSON with Padding)。注意,使用這種方法時,你必須在頁面中定義回調函數,就像上例中的showPrice同樣。

咱們一般所說的JSONP服務(遠程JSON服務),實際上就是一種擴展的支持在用戶定義函數中包含返回數據的能力。這種方法依賴於必須接受一個回調函數的名字做爲參數。

而後執行這個函數,處理JSON數據,並顯示在客戶頁面上。

 


JSONP的客戶端具體實現:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script type="text/javascript"src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script>
        alert(jQuery)
    </script>
</head>
<body>
</body>
</html>

經過script是src加載遠程的jQuery毫無疑問是能夠正常運行的,因此不難發現Web頁面上調用js文件時則不受是否跨域的影響

固然不只如此,咱們還發現凡是擁有"src"這個屬性的標籤都擁有跨域的能力,好比<script>、<img>、<iframe>等

 


在進一步咱們換成契約式接口

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script type="text/javascript"src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script type="text/javascript">
        function remoteLoad(data){
                alert(data) //遠程數據
        }
    </script>
</head>
<body>
</body>
</html>


http://code.jquery.com/jquery-1.11.1.min.js 文件中執行
remoteLoad('加載的數據')

顯而易見OK了,經過加載遠程的腳本到本地中執行,很好的繞開了跨域的問題了,可是這樣的請求是有問題的,接口是契約式的?

怎麼讓遠程js知道它應該調用的本地函數叫什麼名字呢?畢竟是jsonp的服務者都要面對不少服務對象,而這些服務對象各自的本地函數都不相同啊?咱們接着往下看。

 


更進一步增長動態回調

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
        <script type="text/javascript">
            var remoteLoad= function(data){};
            var url = "http://code.jquery.com/jquery-1.11.1.min.js?code=1111&callback=remoteLoad";
             var script = document.createElement('script');
           script.setAttribute('src', url);
             document.getElementsByTagName('head')[0].appendChild(script); 
       </script>
</head>
<body>

</body>

再也不直接把遠程js文件寫死,而是編碼實現動態查詢,而這也正是jsonp客戶端實現的核心部分,本例中的重點也就在於如何完成jsonp調用的全過程。

咱們看到調用的url中傳遞了一個callback參數則告訴服務器,個人本地回調函數叫作remoteLoad,因此請把查詢結果傳入這個函數中進行調用。

 

因此總結其實json的一個核心點:容許用戶傳遞一個callback參數給服務端,而後服務端返回數據時會將這個callback參數做爲函數名來包裹住JSON數據,這樣客戶端就能夠隨意定製本身的函數來自動處理返回數據了。

 


基本原理OK了,咱們看看jQuery的實現,其實也大同小異

$.ajax({
    url           : "remoteLoad.js",
    dataType      : "jsonp",
    jsonp         : "callback", //傳遞給請求處理程序或頁面的,用以得到jsonp回調函數名的參數名(通常默認爲:callback)
    jsonpCallback : "Handler", //自定義的jsonp回調函數名稱,默認爲jQuery自動生成的隨機函數名,也能夠寫"?",jQuery會自動爲你處理數據
    success: function(data) {
        console.log(arguments)
    }
});

jQuery的區別最大的不一樣的就自動幫你生成回調函數並把數據取出來供success屬性方法來調用,不是傳遞的一個回調句柄

 

篇幅比較長了了 下章再合併講解內部實現及請求分發器

相關文章
相關標籤/搜索