上文,我們了解了網(wǎng)絡(luò)世界是如何交換信息的,用什么方式去交互信息(http),HTTP請(qǐng)求過(guò)程是如何實(shí)現(xiàn)的,什么是http請(qǐng)求,什么是http響應(yīng),說(shuō)了那么多,好像還沒(méi)說(shuō)到Ajax,不要急,下面我們就來(lái)實(shí)戰(zhàn)Ajax。
本文梗概:
Ajax實(shí)現(xiàn)的核心對(duì)象
- 牛刀小試——向服務(wù)器發(fā)送Ajax請(qǐng)求
- 更進(jìn)一步——獲取服務(wù)器返回的數(shù)據(jù)
- 開(kāi)始入門(mén)——了解異步與同步
- 再說(shuō)POST——發(fā)送POST請(qǐng)求
- Ajax總結(jié)
Ajax實(shí)現(xiàn)的核心對(duì)象
Ajax即“Asynchronous Javascript And XML”(異步JavaScript和XML),實(shí)際上跟XML沒(méi)多大關(guān)系,XML只是Ajax傳輸?shù)囊环N數(shù)據(jù)形式,也可以是JSON。實(shí)現(xiàn)它需要用到j(luò)avascript中的一個(gè)對(duì)象 XMLHttpRequest,簡(jiǎn)稱(chēng)XHR對(duì)象。
1. 牛刀小試——向服務(wù)器發(fā)送Ajax請(qǐng)求
PHP頁(yè)面的準(zhǔn)備
要學(xué)習(xí)Ajax,就需要和PHP打交道,在后臺(tái)接收我們發(fā)送的請(qǐng)求,下面寫(xiě)兩句PHP代碼,用來(lái)接收我們發(fā)送的Ajax請(qǐng)求
<?php
header("Content-type:text/html;charset=utf-8");
if(!empty($_GET)){
echo '你的數(shù)據(jù)我已經(jīng)接收到了,祝你生活愉快';
}
?>
把這個(gè)頁(yè)面另存為server.php即可。
Javascript代碼的準(zhǔn)備
window.onload = {
document.onclick = function (){
var xhr = new XMLHttpRequest(); //創(chuàng)建XHR對(duì)象
xhr.open('get','server.php',false);
xhr.send(null);
}
}
XHR對(duì)象有一個(gè)open方法,這個(gè)方法用于啟動(dòng)一個(gè)請(qǐng)求,以備發(fā)送。此方法有兩個(gè)必須的參數(shù):
a. 第一個(gè)是指定請(qǐng)求方式,get還是post[此參數(shù)大小寫(xiě)不敏感]
b. 第二個(gè)是發(fā)送請(qǐng)求的url,就是你要發(fā)送請(qǐng)求的那個(gè)頁(yè)面
c. 這里指定的第三個(gè)參數(shù)為發(fā)送異步請(qǐng)求還是同步請(qǐng)求,默認(rèn)為true,異步請(qǐng)求。
XHR對(duì)象的send方法則是將請(qǐng)求數(shù)據(jù)發(fā)送到服務(wù)器上,其參數(shù)有以下規(guī)定:
a. 如果使用GET方法,則參數(shù)為null,因?yàn)镚ET方法使用url傳參。
b. 如果使用POST方法,則參數(shù)為要發(fā)送的數(shù)據(jù)。
把html頁(yè)面和server.php服務(wù)器腳本丟到本地服務(wù)器中去測(cè)試 | php本地服務(wù)器如何搭建?
使用瀏覽器打開(kāi)你的HTML頁(yè)面,注意要在本地服務(wù)器打開(kāi)。然后打開(kāi)開(kāi)發(fā)者工具查看請(qǐng)求。刷新一下,就能看到面板上有一個(gè)HTML文件,點(diǎn)擊一下頁(yè)面,就可以看到一個(gè)請(qǐng)求發(fā)送到了服務(wù)器
2. 更進(jìn)一步——獲取服務(wù)器返回的數(shù)據(jù)
請(qǐng)求已經(jīng)發(fā)送給服務(wù)器了,如無(wú)意外,服務(wù)器很快就可以給我們作出響應(yīng)并返回?cái)?shù)據(jù)了,那如何獲取服務(wù)器返回的數(shù)據(jù)呢?
使用XHR對(duì)象的responseText屬性
只有在send方法后面加上下面這段代碼就可以獲得響應(yīng)數(shù)據(jù)了。
alert(xhr.responseText);
但是實(shí)際上我們不一定都能夠成功地把請(qǐng)求發(fā)送到服務(wù)器的,所以必須要進(jìn)行判斷才能使用服務(wù)器的響應(yīng)數(shù)據(jù),怎么判斷?使用http狀態(tài)碼。
如果服務(wù)器成功接收請(qǐng)求,那么就會(huì)給客戶(hù)端返回一個(gè)200狀態(tài)碼,表示已經(jīng)成功接收請(qǐng)求,所請(qǐng)求的和響應(yīng)頭信息和數(shù)據(jù)隨即返回。
如果我們發(fā)送請(qǐng)求的地址錯(cuò)誤了,那么服務(wù)器就會(huì)返回一個(gè)404狀態(tài)碼,表示所請(qǐng)求資源不存在。所以在獲取響應(yīng)數(shù)據(jù)之前做一下判斷處理可以增強(qiáng)用戶(hù)體驗(yàn)
if(xhr.status == 200){
alert(xhr.responseText);
}else if(xhr.status == 400){
alert('你所請(qǐng)求的資源不存在');
}
——但是返回的為什么是空的呢?因?yàn)槲覀兊恼?qǐng)求的數(shù)據(jù)都沒(méi)有發(fā)送!
剛剛說(shuō)了,GET方法使用url傳參,那么我們?cè)谠O(shè)置url的時(shí)候應(yīng)該設(shè)置我們的參數(shù)
所以我們應(yīng)該這樣設(shè)置open方法
open('get','server.php?123',false);
//向服務(wù)器發(fā)送123這個(gè)字符串
設(shè)置之后就能功能打印出服務(wù)器返回的數(shù)據(jù) "你的數(shù)據(jù)已經(jīng)接收,祝你生活愉快",這就是簡(jiǎn)單的客戶(hù)端與服務(wù)器端的交互。
這種交互是實(shí)時(shí)的,也就是說(shuō)你在服務(wù)器改動(dòng)數(shù)據(jù),客戶(hù)端無(wú)需刷新,依舊能得到服務(wù)器端的最新數(shù)據(jù)。(不信你自己改改那個(gè)字符串)。
但是在IE瀏覽器和火狐瀏覽器上,卻不能。原因是這些瀏覽器默認(rèn)使用了緩存數(shù)據(jù),當(dāng)我們?cè)俅伟l(fā)送請(qǐng)求的時(shí)候,瀏覽器不會(huì)重新從服務(wù)器上下載數(shù)據(jù),而是使用本地緩存的數(shù)據(jù),所以我們應(yīng)該在url上加上一個(gè)生成隨機(jī)數(shù)的js函數(shù),讓提交的請(qǐng)求url每次都不一樣,瀏覽器就不會(huì)使用緩存數(shù)據(jù)了。
open('get','server.php?rand='+Math.random(),false);
3. 開(kāi)始入門(mén)——了解異步與同步
Ajax真容易,就這樣就實(shí)現(xiàn)了?并不是!Ajax的精妙在于異步。那什么是同步,什么是異步呢?
解決這個(gè)問(wèn)題,先做一個(gè)小小的實(shí)驗(yàn)先。
在server.php文件中加上一個(gè)行代碼
<?php
header("Content-type:text/html;charset=utf-8");
sleep(5); //加上這句
if(!empty($_GET)){
echo '你的數(shù)據(jù)我已經(jīng)接收到了,祝你生活愉快';
}
?>
然后在HTML頁(yè)面上觸發(fā)請(qǐng)求。
是不是要等5秒,提示才能彈出來(lái)?那是因?yàn)?秒后,服務(wù)器才給你返回響應(yīng)數(shù)據(jù),剛才那個(gè)代碼是讓服務(wù)器睡眠5秒,再執(zhí)行代碼。
這個(gè)睡眠5秒有什么用?
因?yàn)樵谔斐暇W(wǎng)的速度大家都明白,很多時(shí)候打開(kāi)一個(gè)網(wǎng)站如果網(wǎng)絡(luò)有延遲的話,可能要等待幾秒才能打開(kāi),所以這個(gè)睡眠5秒其實(shí)就是模擬網(wǎng)絡(luò)延遲。
先把sleep(5)注釋掉,點(diǎn)擊HTML頁(yè)面5次,連續(xù)彈出5個(gè)提示,如果不注釋那句代碼,連續(xù)點(diǎn)擊HTML頁(yè)面5次,會(huì)發(fā)生什么事?很顯然,需要等待25秒才能把提示全部彈出來(lái)。所以,同步[此例open方法設(shè)置同步] 請(qǐng)求需要等待服務(wù)器端響應(yīng)后才能發(fā)起第二次請(qǐng)求,這個(gè)等待服務(wù)器響應(yīng)的過(guò)程,客戶(hù)端是什么都不能干的,甚至?xí)霈F(xiàn)瀏覽器假死的狀況(卡住)
同步用戶(hù)體驗(yàn)糟糕透了,所以一般都不會(huì)使用同步請(qǐng)求,那異步請(qǐng)求又是怎么的?
其實(shí)很簡(jiǎn)單,只需要把open方法的false,改為true就可以了!
但是我們可以發(fā)現(xiàn)改了之后,點(diǎn)擊頁(yè)面一次,HTML頁(yè)面輸出的是第一次是空字符串,第二次才是響應(yīng)數(shù)據(jù),第三次還是響應(yīng)數(shù)據(jù),這是為什么?
因?yàn)閷?duì)于異步請(qǐng)求,瀏覽器將數(shù)據(jù)傳輸交給瀏覽器后臺(tái)處理,我們發(fā)送了請(qǐng)求,就馬上打印輸出數(shù)據(jù),當(dāng)然為空,要等到瀏覽器把數(shù)據(jù)完全接收完數(shù)據(jù)才能夠打印輸出。而同步之所以能夠打印輸出,是因?yàn)樗緛?lái)就必須等到客戶(hù)端完全接收數(shù)據(jù)才能夠?qū)?shù)據(jù)進(jìn)行處理,期間不能進(jìn)行任何操作,而異步在接收數(shù)據(jù)的同時(shí),是可以進(jìn)行其他操作的,比如說(shuō),我們?cè)诘却憫?yīng)的同時(shí),可以繼續(xù)發(fā)送請(qǐng)求。
簡(jiǎn)單的總結(jié)就是:
同步請(qǐng)求必須等到服務(wù)器器完全響應(yīng)了才能發(fā)起下一次請(qǐng)求
異步請(qǐng)求在客戶(hù)端等待服務(wù)器響應(yīng)的同時(shí)就可以發(fā)起下一次請(qǐng)求
5個(gè)同步請(qǐng)求完成需要25秒,5個(gè)異步請(qǐng)求完成,大概只需要6、7秒。
還有一個(gè)問(wèn)題沒(méi)有解決,為什么會(huì)彈出多次?
因?yàn)閷?duì)于異步請(qǐng)求,瀏覽器會(huì)有一個(gè)就緒狀態(tài),表明數(shù)據(jù)接收的狀態(tài)。一般我們使用就緒狀態(tài)事件監(jiān)聽(tīng)函數(shù)來(lái)捕獲這些狀態(tài)信息,再對(duì)數(shù)據(jù)進(jìn)行處理。
window.onload = {
document.onclick = function (){
var xhr = new XMLHttpRequest(); //創(chuàng)建XHR對(duì)象
xhr.onreadystatechange = function (){
alert(xhr.readyState+'\n'+xhr.responseText);
}
xhr.open('get','server.php?rand='+Math.random()+'&num=123',true);
xhr.send(null);
}
從上面的例子可以看出來(lái),當(dāng)readyState屬性的值為3和4的時(shí)候,才能接收到服務(wù)器的數(shù)據(jù)。
readystate是指客戶(hù)端在接收http響應(yīng)數(shù)據(jù)的時(shí)候的狀態(tài)
0表示XHR對(duì)象尚未建立
1表示尚未調(diào)用open方法
2表示已經(jīng)調(diào)用open方法,但是還未調(diào)用send方法發(fā)送
3.表示已經(jīng)調(diào)用send方法,但是返回的數(shù)據(jù)還不完整,尚且不能使用
4.表示已經(jīng)接收數(shù)據(jù)完畢,數(shù)據(jù)已經(jīng)可以使用。
所以正確使用異步請(qǐng)求的正確姿勢(shì),應(yīng)該是這樣的
window.onload = {
document.onclick = function (){
var xhr = new XMLHttpRequest(); //創(chuàng)建XHR對(duì)象
xhr.onreadystatechange = function (){
if(xhr.readystatechange == 4 && xhr.status == 200){
alert(responseText);
}
}
xhr.open('get','server.php?rand='+Math.random()+'&num=123',true);
xhr.send(null);
}
4. 再說(shuō)POST——發(fā)送POST請(qǐng)求
上面一直都是使用GET方式發(fā)送請(qǐng)求,怎么樣發(fā)送POST方式請(qǐng)求呢?
先看代碼
window.onload = {
document.onclick = function (){
var xhr = new XMLHttpRequest(); //創(chuàng)建XHR對(duì)象
xhr.onreadystatechange = function (){
if(xhr.readystatechange == 4 && xhr.status == 200){
alert(responseText);
}
}
xhr.open('post','server.php?rand='+Math.random(),true);
//第一處改動(dòng),將get設(shè)置為post
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
// 第二處改動(dòng),設(shè)置了請(qǐng)求頭
xhr.send('name=123');
//第三處改動(dòng),send方法參數(shù)添加數(shù)據(jù)。
}
對(duì)于第二處的解析:
服務(wù)器對(duì)于POST請(qǐng)求的解析方式和web表單提交是不同的所以,需要模擬表單進(jìn)行提交。[此處搞得不太明白。]
對(duì)于第三處的解析:
POST方法提交數(shù)據(jù)是模擬表單的形式發(fā)送的,所以需要使用鍵值對(duì)的方式提交服務(wù)器端才能接收。
POST請(qǐng)求的優(yōu)點(diǎn)與缺點(diǎn):
優(yōu)點(diǎn):
- 數(shù)據(jù)大小無(wú)限制,GET請(qǐng)求適合在服務(wù)器端查詢(xún)獲取數(shù)據(jù),而POST請(qǐng)求適合發(fā)送數(shù)據(jù)
- 數(shù)據(jù)被存放在實(shí)體中,不直接暴露在外,較GET方法安全。
缺點(diǎn):
- 性能上沒(méi)有GET方法快,因?yàn)榉?wù)器要對(duì)POST請(qǐng)求做特殊的處理。
5. Ajax總結(jié)
GET請(qǐng)求:
- 新建XHR對(duì)象
- 使用open方法啟動(dòng)請(qǐng)求,以備發(fā)送
- 使用就緒狀態(tài)監(jiān)聽(tīng)函數(shù),當(dāng)數(shù)據(jù)完全接收的時(shí)候,觸發(fā)函數(shù),獲取響應(yīng)數(shù)據(jù)
- 使用send方法發(fā)送請(qǐng)求,參數(shù)為null
[疑問(wèn):為什么就緒狀態(tài)監(jiān)聽(tīng)函數(shù)要寫(xiě)在sen方法前面呢?實(shí)際上寫(xiě)在后面也是可以的,但是寫(xiě)在前面表示先把這個(gè)方法載入內(nèi)存,觸發(fā)時(shí)調(diào)用,是個(gè)好習(xí)慣]
POST請(qǐng)求:
- 新建XHR對(duì)象
- 使用open方法啟動(dòng)請(qǐng)求,以備發(fā)送
- 設(shè)置http頭信息的Content-Type類(lèi)型,模擬表單發(fā)送
- 使用就緒狀態(tài)監(jiān)聽(tīng)函數(shù),當(dāng)數(shù)據(jù)完全接收的時(shí)候,觸發(fā)函數(shù),獲取響應(yīng)數(shù)據(jù)
- 使用send方法發(fā)送請(qǐng)求,參數(shù)為要發(fā)送的數(shù)據(jù)
寫(xiě)了那么多好像發(fā)現(xiàn)文字真的是太蒼白了,就算是我自己也會(huì)直接看教程而不是看這些無(wú)聊而神煩的文字,哈哈,算了,當(dāng)自己總結(jié)好了……下次盡量精簡(jiǎn)一些