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


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

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

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

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

 


script 類型

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

根據API的說明可知,如果dataType類型為script的時候,需要處理

1 執行腳本

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屬性方法來調用,不是傳遞的一個回調句柄

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM