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吧


免責聲明!

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



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