jQuery Ajax
前言
相信很多有一定前端經驗的同學肯定聽說過 jQuery,并且稍有研究的人肯定也知道 jQuery 提供了 Ajax 系列的工具方法。
事實上,市面上有各種各樣的 Ajax 類庫可以供我們使用,它們更加健壯、更加成熟、并且設計也更為合理。在我們在實際開發(fā)的過程中,我們并不直接使用 XMLHttpRequest,而是會使用這些封裝好的經得起考驗的類庫,來為我們的程序提供服務,讓我們的開發(fā)者更加專注在本身的業(yè)務上。
那么,本章節(jié)挑選了曾經盛極一時的 jQuery 的工具方法 Ajax 來做展開介紹。
首先,我先引用 jQuery 的設計宗旨:
Write less, do more.
稍有研究的同學都知道 jQuery 非常好用,這些都得益于 jQuery 優(yōu)秀的設計思想。它不但能夠提供便捷的元素選擇及操作方式,還提供了一系列的工具方法、鏈式操作等等,使得我們在開發(fā)過程中變得足夠簡易。Ajax 同樣繼承了 jQuery 的優(yōu)秀設計思想,提供了更為人性化的接口和配置。接下來我們來聊聊 jQuery Ajax :
1.引入 Jquery
因為我們使用的 Ajax 方法是 jQuery 提供的,因此我們需要在頁面中引入 jQuery 腳本。
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
Tips: 注意 jQuery 腳本要放在使用到 jQuery 的腳本之前,這樣才可以在我們的頁面中愉快的玩耍~
2.簡單的 load
load(url, [data], [callback])
load 是 jQuery 一個非常便捷的 Ajax 交互方法,從接口文檔上看,load 方法接受三個參數,分別是 url,發(fā)送的參數以及響應結果的回調函數。實際上,后兩個參數我們也常??梢院雎?。
我們先來看一個簡單的例子:
假設我們要在頁面中插入一個從后端請求回來的數據,那么我們會怎么做?
有同學非常聰明,可能會說:我先啟用 ajax ,發(fā)送 GET 請求,在成功的時候回調函數里面再操作頁面的元素對象,為 innerHTML 賦值結果。
是的,基本流程就是查詢之后再操作元素添加數據即可。但是,jQuery 的 load 可能更加簡單哦。不信請看:
2.1 后端返回數據
router.get("/jquery_ajax/load", function(req, res) {
res.send('<p>返回的內容</p>');
});
2.2 前端關鍵代碼
<div id="container"></div>
$('#container').load('/jquery_ajax/load')
2.3 看看效果
怎么樣,只需要一行,我們就可以往目標元素中添加數據了。這確實是非常的方便!
3.$.ajax
事實上, load 屬于基于 $.ajax 再次封裝的一個簡易的 API 。有興趣的同學可以去扣一下源碼來看看。大概實現就是這樣:
jQuery.fn.load = function( url, params, callback ) {
var selector, type, response,
self = this,
off = url.indexOf( " " );
// xxx
// If we have elements to modify, make the request
if ( self.length > 0 ) {
jQuery.ajax( {
// xxx
} ).done( function( responseText ) {
// xxx
} ).always( callback && function( jqXHR, status ) {
// xxx
} );
}
return this;
};
這里我隱去了一些具體實現,目的只是為了說明 load 內部的基本實現是怎么樣的即可。對具體實現有興趣的同學可以自行讀一下源代碼。
事實上,load 的定制化相對較高,必定無法滿足更為靈活的需求。因此,我們多數時候會使用 jQuery 封裝好的更為底層的 API —— $.ajax。
3.1 API 介紹
jQuery.ajax(url,[settings])
如上所示,$.ajax 接受兩個參數,分別為 url 和 配置對象。其中:
- url : 一個用來包含發(fā)送請求的URL字符串。
- [settings] : 一個以
{ key : value }
組成的 Ajax 請求設置對象。所有選項都是可選的。
在配置對象中,我們通常會傳入我們希望指定的配置項,來指定我們的 Ajax 的行為和屬性。當然有些參數我們并不一定要去指定,因為有的配置項本身會有默認配置,只要該默認配置符合你的使用需求即可。有興趣的同學也可以到 jQuery Ajax 詳細查看每個配置項的默認值以及含義。
3.2 常用配置項簡介
常用的配置項我們簡單帶過,比如:
- data:代表著發(fā)送到服務器的數據。 在不同的情況下會轉化字符串的格式,在 GET 方法的時候,會變?yōu)?“&” 拼接的參數附帶在 url 后面。
- dataType:預期服務器返回的數據類型。 如果沒有指定的話,Ajax 會根據 HTTP 的 MIME 信息來進行判斷。
- cache:緩存控制相關。 默認是 true,如果設置為 false,那么瀏覽器不緩存此頁面。
- headers:請求頭。 我們通常會給一個
{ key : value}
這樣的鍵值對對象來設置我們的請求頭內容。 - type:請求方法。默認是 GET 。
3.3 階段處理
$.ajax 提供了非常人性化的函數配置項,分別有:beforeSend、error、dataFilter、success、complete。我把它們分別歸為請求前和請求后兩個階段。
3.3.1 請求前
對于一個 Ajax 的請求來說,我們在正式發(fā)送請求之前可能會對我們的 Ajax 做一些提前的配置或者預先要進行的操作。那么,我們可以非常愉快的使用 beforeSend 。
3.3.1.1 beforeSend
beforeSend(xhr)
beforeSend 指定的是發(fā)送請求之前要先執(zhí)行的方法。 并且,jQuery 會在 beforeSend 中傳入當前的 XMLHttpRequest 對象,方便我們在該函數中做預先的操作。打個比方,我們可以通過這個函數,給當前的 Ajax 請求添加自定義的 headers 項。
3.3.2 請求后
當我們成功的發(fā)送了請求,那么我們接下來就是要解決返回結果的問題了。
3.3.2.1 dataFilter 和 success
在服務器成功響應之后,我們需要對結果進行獲取和處理,這個時候 dataFilter 和 success 就派上用場了。其中:
dataFilter :
dataFilter (Object data, String type)
dataFilter 接受一個服務端返回的原始數據和一個 dataType 值作為參數,會在請求成功之后調用,目的是為了我們可以在請求成功之后對數據進行進一步的處理、過濾。 對比另外一個函數 —— success。
success:
success(( Object data, String textStatus, jqXHR jqXHR ))
success 是成功響應后執(zhí)行的函數,優(yōu)先級在 dataFilter 之后。 它接受三個參數,分別是:數據對象、狀態(tài)描述字符串以及一個 XHR 對象。我們通常會在這個函數里面進行對結果的操作。
3.3.2.2 錯誤處理
當然也會有請求失敗的情況,那么我們該如何來處理呢?
$.ajax 同樣提供了人性化的一個回調函數 error 。
error(jqXHR jqXHR, String textStatus, String errorThrown)
error 接受三個參數,分別為 一個 XHR 對象、一個狀態(tài)描述字符串以及一個捕獲異常的字符串。假如我在程序中定義了這樣一個 $.ajax :
$.ajax({
url: '/jquery_ajax/get/2',
method: 'GET',
error (xhr, status, err) {
console.log(xhr, status, err)
}
})
其中,我們的 url 指定的是一個不存在的接口,那么我們可以在控制臺看到這樣的打印信息:
顯而易見,我們可以很輕易在這個函數中獲取到請求的錯誤信息。事實上,我們常常會在 error 中對異常做進一步的操作。舉個栗子,在我們開發(fā)的應用中,我們不可能把請求失敗的這樣一個結果自己在程序中抹干凈不上報給用戶知道,如果我們內部自己消化了,用戶不知道自己到底成不成功,這樣的體驗就非常糟糕了!這個時候,我們或許會選擇彈一個框,告知我們失敗的結果以及下一步的操作。
3.3.2.3 最后處理
好了,來繼續(xù)思考這樣一個場景:假如我們發(fā)起一個 $.ajax()
,為了讓用戶感知,我們一般會彈起一個 loading 的界面,讓用戶稍微等待一下。那問題來了,我們的 loading 界面到底什么時候應該關閉呢?是在請求成功之后關閉嗎?那如果請求發(fā)生錯誤怎么辦?總不能錯誤了就不關閉 loading 了吧?那或者在成功和失敗的回調中都執(zhí)行關閉 loading 的方法,這貌似是一個不錯的做法,但是這樣的做法我們要在兩個地方都進行改動,未免太過麻煩,并且后期代碼也不好維護呢。
別忘了,我們還有一個 complete。
complete(jqXHR jqXHR, String textStatus)
complete 會在請求完成之后執(zhí)行,無論請求成功或者失敗。 我們可以很好地將一個請求的善后工作交給它來進行。
4.$.ajax 和 deferred
在 jQuery 中,通常都會有一些耗時比較長的操作,我們是無法馬上得到結果的。對于 $.ajax 而言,我們的請求往往都要依靠網絡傳輸和服務端的處理。這時我們的一貫處理方式就是給定某個回調函數,當耗時操作結束返回數據以后,及時調用這個回調函數來進行進一步的處理。
在 jQuery 1.5 版本之后就改變了這一點,deffered 在 jQuery 中誕生了!
字面來看,defer 代表的是“延遲”的意思,這也對應著我們的延時操作。使用 deffered ,我們將改變使用回調函數的做法,轉而使用的是鏈式調用。這其實也正是擁抱了 jQuery 的設計思想,使用過 jQuery 的同學都知道,jQuery 對節(jié)點的操作往往都會封裝為一個 jQuery 的對象進行統一管理,而 deffered 也正是如此,會對延時操作做一個統一的管理,并且提供統一的編程接口,比如 done 和 fail。
舉個栗子,我們使用 $.ajax 來進行數據的請求,可以變體為:
$.ajax({
url: '/jquery_ajax/get',
method: 'GET',
data: {
a: '123',
b: '234'
}
}).done(data => {
console.log(data)
}).fail(err => {
console.log(err)
})
事實上在 deffered 出來之后,$.ajax 也開始返回一個 deffered 對象。細心的同學可以看到,這不是一個 promise 么?是的,可以這樣說,但是也不盡然,jQuery 根據自己的需要實現了這樣一個東西,它支持 promise 的一些規(guī)范,同時也支持自己的特權方法。我們在使用 deffered 的時候,很直觀的可以看到,我們從回調函數的傳統方式中解脫了出來。除此之外,由于 Promise 的反控制反轉,使得我們的程序變得更加健壯,我們對結果操作更有掌控權。
5.總結
- 市場上有很多 Ajax 類庫,它們更加成熟、設計更加合理,在實際工作中可以首選使用。
- jQuery 提供了非常簡單易用的 Ajax 工具方法,包括簡單的 $.load 方法,還有更為靈活也更為底層的 $.ajax 方法。
- deferred 能夠很好的應用于一些耗時操作,并且提供統一的編程接口。
- deffered 改變了以往 $.ajax 的處理方式,使得我們可以通過鏈式調用的方式來進行結果的操作,既改變了回調函數的老式做法,也讓我們對結果操作更有掌控權。