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