jQuery源碼分析系列(30) : Ajax 整體結構


開頭引用一段

想起一句話:前端研究,研究個屁~ 的確如此呀。補充下聯:前端設計,設計個屁~

前端目前最大的困境是,如 HTML 一樣,無論你承不承認,市場上並不太需要 HTML 高手

其實這里引發一個問題:前端的價值究竟是什么?未來應該如何發展?

我個人覺得還是一個核心價值的問題,前端在漂亮的東西都是需要后端的數據支撐的,而且前端的絕大部分問題,其實都需要后端才能解決,就如我開發了三年的混合項目,雖然前端代碼有3萬行,但是后端一個數據拷貝失敗,整個項目都是白搭

當然這里我並非要深入這個話題,只是想表明盡可能的提高自身的價值

從前端的歷史變更與發展過程來看,如果拿人類的歷史來比喻,從最原始的刀耕火種以HTML靜態結構為主的原始社會,到后來帶有邏輯處理與服務器混搭交互的的石器時代以及最近幾年流行的node.js,MVC,MVVM,Phonegap等衍生而來的東東讓前端大躍進式的跳躍進入了一個新的工業時代,不到20年的時間web技術翻天覆地的變化

如今的前端進入了 MV* 時代,同時Ajax也帶來了SPA,Node帶來了全棧,這些都切實推動着前端往前發展

優秀的前端一方面是完成的效率,另一方面是可維護和可擴展性的提高帶來的潛在價值,解放生產力,專注展現與業務邏輯,減少瑣碎的兼容處理

如果你使用一面大鏡子作為沖浪板會發生什么?或許你會在較短的時間內征服海浪,但是你肯定從內心深處明白,這不是沖浪的正確選擇

所以我們不應該只要求自己僅僅停留在API的層次,如果能理解原理深入設計,那么就將會跨上一個新的台階

 


jQuery.Ajax做了那些事?

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

關於AJAX基礎請飛機 觸碰jQuery:AJAX異步詳解

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

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的修復等等

深入待續。。。。。


免責聲明!

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



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