1. 程式人生 > >jQuery原始碼分析系列 : Ajax 整體結構

jQuery原始碼分析系列 : Ajax 整體結構

jQuery.Ajax做了那些事?

我們知道AJAX的底層實現其實是很簡單的.拋開IE不說,標準的w3c直接提供了XMLHttpRequest方法

我們主要站在設計的角度理解,如何設計出低耦合高內聚的程式碼

jQuery對Ajax的處理主要體現在對瀏覽器相容,資料的處理及過濾,各種事件的封裝上

主要有以下幾部分擴充套件:

提供快捷介面

提供底層介面

提供資料序列化

提供全域性Ajax事件處理

具體使用請看API,這裡不再重複

分析下面一個demo

給document繫結ajaxStart,ajaxComplete回撥事件

trigger繫結一個點選事件,傳送ajax請求

點選trigger出發點之後,傳送一個ajax請求,並且通過complete,done,ajaxStart, ajaxComplete返回狀態回撥

複製程式碼
//全域性事件觸發
$(document).ajaxStart(function() {
    console.log(arguments)
}).ajaxComplete(function() {
    $(".log").text("Triggered ajaxComplete handler.");
});


$(".trigger").click(function() {
    //傳送ajax請求
    //
    $.ajax({
        url: 
"php.html", context: document.body, complete: function() { console.log(this) } }).done(function() { console.log(this) }); });
複製程式碼

這裡實現比較特別的地方

針對ajax提供2種回撥方式,內部的complete回撥與外部的done回撥

全域性document上都能捕獲到ajax的每一步的回撥通知

換句話說,針對ajax的請求,每一步的狀態,成功或者失敗,我們有3種方式可以獲取,但是每一種還是有各自的區別

1 ajax的引數回撥

2 基於deferred方式的done回撥

3 全域性的的自定義事件的回撥

從設計的層面上來考下,這種事件組合的方式是如何實現?有什麼優勢?

設計一:

tAjax({
    url: "php.html",
    complete: function(data) {
        console.log(data)
    }
})

如果要實現這種介面呼叫

那麼我們就需要封裝下程式碼,把回撥通過實參傳遞

複製程式碼
var tAjax = function(config) {
    var url      = config.url;
    var complete = config.complete; 
    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
    xhr.open('post', url);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.status == 200) {
                complete(xhr.responseText);
            }
        }
    }
    xhr.send();
}
複製程式碼

這樣設計可以看做類似工廠模式的封裝,好處不用多說,在工廠模式裡面包含了物件的建立等必要的邏輯,客戶端根據傳參選擇動態的例項化相對的處理

對於客戶端來去除了具體的依賴,當然tAjax你也可以看作一個外觀模式提供的介面,其實就是隱藏了具體的複雜邏輯,提供一個簡單的介面,從而降低耦合

設計二:

複製程式碼
tAjax({
    url: "php.html",
    complete: function(data) {
        console.log(data)
    }
}).done(function(data){
    console.log(data)
})
複製程式碼

在之前加入了一個done鏈式處理,當然這裡done,其實是deferred的一個成功處理通知,如果之前沒有接觸,大家去了解一下關於deferred的概念

我們知道jQuery實現了鏈式,實現的原理無法就是返回本身物件的引用

複製程式碼
var ajax = tAjax({
    url: "php.html",
    complete: function(data) {
        console.log(data)
    }
})

ajax.done(function(){
    
})
複製程式碼

以上是分離的情況下,如果要合併成一條鏈式處理,只要在上一個方法中返回this即可

所以我們改改

複製程式碼
var tAjax = function(config) {

    var doneFn;

    var url      = config.url;
    var complete = config.complete; 
    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
    xhr.open('post', url);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.status == 200) {
                doneFn(xhr.responseText);
                complete(xhr.responseText);
            }
        }
    }

    xhr.send(xhr.responseText);

    return {
        /**
         * 返回一個done物件
         */
        done: function(ourfn) {
             doneFn = ourfn;
        }
    };
}
複製程式碼

我們返回了一個done物件,這裡一樣要是物件,因為鏈式的原因,我們看外部指定了內部的done,從而把外部函式給引用到內部的doneFn上 快取起來

xhr.staturs 成功後一起執行

當然這種設計是有問題的,如果在done之後我在鏈式就肯定不行,因為物件的引用錯了,那麼jQuery是如何處理?

設計三

提供document物件的全域性處理

複製程式碼
$(document).ajaxComplete(function() {
    console.log('ajax請求成功')
});


tAjax({
    url: "php.html",
    complete: function(data) {
         console.log(data)
    }
}).done(function(data){
    console.log(data)
})
複製程式碼

這裡的問題就是把ajax內部的事件,返回給全域性捕獲了,有點類似css3的全域性動畫事件

這裡的設計其實最簡單了因為本身之間沒有什麼耦合,就是傳送一個事件給document即可

jQuery利用了trigger自定義事件觸發的

globalEventContext.trigger("ajaxComplete", [jqXHR, s]);

具體每一種實現在後面的都會提到,在這裡需要大家有個整體的印象,

總結:

通過讀閱jQuery.ajax這段api我們可以看到jQuery對ajax的處理做的相當的全面

首先我們經常遇到某些耗時很長的javascript操作。其中,既有非同步的操作(比如ajax讀取伺服器資料),也有同步的操作(比如遍歷一個大型陣列),它們都不是立即能得到結果的。

通常的做法是,為它們指定回撥函式(callback)。即事先規定,一旦它們執行結束,應該呼叫哪些函式。

ajax引入了deferred方案,callback方案就是回撥函式解決方案,從而處理耗時操作的問題,對那些操作提供了更好的控制,以及統一的程式設計介面

伴隨Ajax請求會觸發若干事件,我們可以訂閱這些事件並在其中處理我們的邏輯。

在jQuery中有兩種Ajax事件:區域性事件和全域性事件。

區域性事件(回撥函式),在$.ajax()方法的options引數中宣告,可以用來設定請求資料和獲取、處理響應資料。

全域性事件,每次Ajax請求都會觸發,它會向DOM中的所有元素廣播,你只需為DOM中任意元素bind好全域性事件即會觸發(若繫結多次,則會依次觸發為事件註冊的回撥函式

除此之外,還提供了一系列的簡化介面,比如.load ,還有直接對資料物件序列化的能力,對跨域的處理,contentType的修復等等