1. 程式人生 > >jQuery原始碼分析系列(33) : AJAX中的前置過濾器和請求分發器

jQuery原始碼分析系列(33) : AJAX中的前置過濾器和請求分發器

jQuery1.5以後,AJAX模組提供了三個新的方法用於管理、擴充套件AJAX請求,分別是:

1.前置過濾器 jQuery. ajaxPrefilter

2.請求分發器 jQuery. ajaxTransport,

3.型別轉換器 ajaxConvert

原始碼結構:

jQuery.extend({

    /**
     * 前置過濾器
     * @type {[type]}
     */
    ajaxPrefilter: addToPrefiltersOrTransports(prefilters),

    /**
     * 請求分發器
     * @type {[type]}
     
*/ ajaxTransport: addToPrefiltersOrTransports(transports), ........................... });

可見這2個方法是通過私有方法addToPrefiltersOrTransports通過curry手段構造的,分別是保持了prefilters與transports的引用

來個簡單的模擬這個結構

var prefilters = 2;

var addToPrefiltersOrTransports = function(prefilters) {
    
return function(b) { return prefilters + b; } } var ajaxPrefilter = addToPrefiltersOrTransports(prefilters) ajaxPrefilter(1) //3

可見ajaxPrefilter就維持了addToPrefiltersOrTransports返回函式的引用了,這種就是閉包的手法了,這也是JS的開發人員都需要掌握的

好處就是合併多個引數,當然因為維持引用代價就是一點點效能消耗

當然jQuery不是傳遞的簡單型別處理,還可以傳遞的一個引用型別的回撥函式,所以針對ajaxPrefilter方法放閉包構件就需要做一些處理了

填充prefilters處理器

var prefilters = {};

var addToPrefiltersOrTransports = function(structure) {

    return function(func) {
        structure['*'] = func;
    }
}

var ajaxPrefilter = addToPrefiltersOrTransports(prefilters)


ajaxPrefilter(function(options){
    return {
        send:function(){

        },
        callback:function(){

        }
    }
})

其實說白了就是把對應的方法制作能函式的形式填充到prefilters或者transports對應的處理包裝物件中

要用的時候直接執行,每個函式都保持著各自的引用

這種寫法的好處自然是靈活,易維護,減少程式碼量

還有我們經常的使用的,jQuery的程式碼很簡練,比如合併多個方法的建立等等

jQuery.each([
        "tabIndex",
        "readOnly",
        "maxLength",
        "contentEditable"
    ], function() {
        jQuery.propFix[this.toLowerCase()] = this;
    });

所以此時的prefilters中的結構就是

prefilters = {
        '*': function() {
            return {
                send: function() {

                },
                callback: function() {

                }
            }
        }
    }

迴歸重點,那麼引入ajaxPrefilter與ajaxTransport的作用是幹嘛?

前置過濾器和請求分發器在執行時,分別遍歷內部變數prefilters和transports,這兩個變數在jQuery載入完畢後立即初始化,從過閉包的方法填充這個2個物件

ajaxPrefilter與ajaxTransport都是通過inspectPrefiltersOrTransports構建器

prefilters中的前置過濾器在請求傳送之前、設定請求引數的過程中被呼叫,呼叫prefilters的是函式inspectPrefiltersOrTransports;

巧妙的是,transports中的請求分發器在大部分引數設定完成後,也通過函式inspectPrefiltersOrTransports取到與請求型別匹配的請求分發器:

function inspectPrefiltersOrTransports(structure, options, originalOptions, jqXHR) {

    var inspected = {},
        seekingTransport = (structure === transports);

    function inspect(dataType) {
        var selected;
        inspected[dataType] = true;
        jQuery.each(structure[dataType] || [], function(_, prefilterOrFactory) {
            var dataTypeOrTransport = prefilterOrFactory(options, originalOptions, jqXHR);
            if (typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[dataTypeOrTransport]) {
                options.dataTypes.unshift(dataTypeOrTransport);
                inspect(dataTypeOrTransport);
                return false;
            } else if (seekingTransport) {
                return !(selected = dataTypeOrTransport);
            }
        });
        return selected;
    }

    return inspect(options.dataTypes[0]) || !inspected["*"] && inspect("*");
}

遍歷structure[dataType]陣列,並執行回撥
prefilterOrFactory為函式陣列元素
執行該函式如果返回的結果dataTypeOrTransport是字串且時prefilters且沒有被inspected過
就給options.dataTypes陣列頭部新增該字串
繼續遞迴dataTypeOrTransport(當我們使用json/jsonp的時候會返回“script”,於是會執行“script”相關的回撥)
如果是transport就返回dataTypeOrTransport的假結果

前置過濾器 prefilters

簡單的說就是一種hack的做法,只是說比起事件的那種hack寫的手法實現更為高明

我們可以看看針對prefilters的方法其實就是dataType為 script,json,jsonp的處理

當我們動態載入指令碼檔案比如

$.ajax({
    type     : "GET",
    url      : "test.js",
    dataType : "script"
});

所以在inspectPrefiltersOrTransports方法中prefilters[script]能找到對應的處理方法,所以就會執行

例如script的hack,要強制加上處理快取的特殊情況和crossDomain

因為設定script的前置過濾器,script並不一定意思著跨域

跨域未被禁用,強制型別為GET,不觸發全域性時間

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

所以prefilters就是在特定的環境針對特定的情況做一些必要的相容的處理

請求分發器 transports

請求分發器顧名思義傳送請求,那麼底層的ajax傳送請求是通過send方法

xhr.send();

但是jQuery對send方法做了拆分,把對應的處理放到了transports中了

那麼transports物件也是類似前置處理器通過jQuery.ajaxTransport構建

例如script,send,abort方法

返回出transports方法

transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);