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