我們平常工作中經(jīng)常會(huì)用到ajax
向服務(wù)器發(fā)送請(qǐng)求蹂喻,今天就來(lái)詳細(xì)的來(lái)了解一下ajax
的前世與今生氨肌!
簡(jiǎn)介
瀏覽器與服務(wù)器之間莺治,采用HTTP
協(xié)議通信廓鞠。用戶在瀏覽器地址欄鍵入一個(gè)網(wǎng)址帚稠,或者通過(guò)網(wǎng)頁(yè)表單向服務(wù)器提交內(nèi)容,這時(shí)瀏覽器就會(huì)向服務(wù)器發(fā)出 HTTP
請(qǐng)求床佳。
1999年滋早,微軟公司發(fā)布 IE 瀏覽器5.0版,第一次引入新功能:允許 JavaScript
腳本向服務(wù)器發(fā)起 HTTP
請(qǐng)求砌们。這個(gè)功能當(dāng)時(shí)并沒(méi)有引起注意杆麸,直到2004年 Gmail
發(fā)布和2005年 Google Map
發(fā)布,才引起廣泛重視浪感。2005年2月昔头,AJAX
這個(gè)詞第一次正式提出,它是 Asynchronous JavaScript and XML
的縮寫(xiě)影兽,指的是通過(guò) JavaScript
的異步通信揭斧,從服務(wù)器獲取 XML
文檔從中提取數(shù)據(jù),再更新當(dāng)前網(wǎng)頁(yè)的對(duì)應(yīng)部分峻堰,而不用刷新整個(gè)網(wǎng)頁(yè)讹开。后來(lái),AJAX
這個(gè)詞就成為JavaScript
腳本發(fā)起 HTTP
通信的代名詞捐名,也就是說(shuō)旦万,只要用腳本發(fā)起通信,就可以叫做 AJAX
通信桐筏。W3C 也在2006年發(fā)布了它的國(guó)際標(biāo)準(zhǔn)纸型。
AJAX 包括以下幾個(gè)步驟。
- 創(chuàng)建XMLHttpRequest
- 發(fā)出 HTTP請(qǐng)求
- 接受服務(wù)器傳回的數(shù)據(jù)
- 更新網(wǎng)頁(yè)數(shù)據(jù)
那么什么是ajax呢
就是一句話梅忌,AJAX
通過(guò)原生的XMLHttpRequest
對(duì)象發(fā)出 HTTP
請(qǐng)求狰腌,得到服務(wù)器返回的數(shù)據(jù)后,再進(jìn)行處理∧恋現(xiàn)在琼腔,服務(wù)器返回的都是 JSON
格式的數(shù)據(jù),XML
格式已經(jīng)過(guò)時(shí)了踱葛,但是 AJAX
這個(gè)名字已經(jīng)成了一個(gè)通用名詞丹莲,字面含義已經(jīng)消失了。
ajax
是一種技術(shù)方案尸诽,但并不是一種新技術(shù)甥材。它依賴的是現(xiàn)有的CSS
/HTML
/Javascript
,而其中最核心的依賴是瀏覽器提供的XMLHttpRequest
對(duì)象性含,是這個(gè)對(duì)象使得瀏覽器可以發(fā)出HTTP
請(qǐng)求與接收HTTP響應(yīng)洲赵。
所以我用一句話來(lái)總結(jié)兩者的關(guān)系:
我們使用XMLHttpRequest
對(duì)象來(lái)發(fā)送一個(gè)Ajax請(qǐng)求。 具體請(qǐng)參考這里:XMLHttpRequest
創(chuàng)建
實(shí)現(xiàn)一個(gè)最簡(jiǎn)單功能的Ajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Ajax</title>
</head>
<body>
<script>
var xhr = new XMLHttpRequest() //創(chuàng)建一個(gè)對(duì)象
xhr.open('GET', '/hello.json', true)//設(shè)置參數(shù),請(qǐng)求的類型叠萍,地址芝发,是否使用異步
//hello.json 可以把他當(dāng)作一個(gè)資源,而不是一個(gè)文件
//true為異步苛谷,false為同步獲取的方式
xhr.send()//發(fā)送請(qǐng)求
//內(nèi)部數(shù)據(jù)到達(dá)會(huì)默認(rèn)觸發(fā)load
xhr.addEventListener('load', function(){
var data = xhr.responseText;
console.log(data);
})//監(jiān)聽(tīng)
</script>
對(duì)請(qǐng)求狀態(tài)進(jìn)行判斷 看是否請(qǐng)求成功
<script>
var xhr = new XMLHttpRequest()
xhr.open('GET', '/hello2.json', true)//請(qǐng)求不存在的文件冷守,404 not found
xhr.send()//發(fā)送請(qǐng)求
//監(jiān)聽(tīng)通信狀態(tài)的變化
xhr.onreadystatechange = function(){
if(xhr.readyState ===4 && xhr.status ===200){
console.log(xhr.responseText);
}
}
xhr.addEvenListener('onreadystatechange' = function(){
console.log('readyState:', xhr.readyState);
//準(zhǔn)備狀態(tài)凌唬,握手的過(guò)程羊赵,每次握手會(huì)發(fā)生改變蹬昌,可以監(jiān)聽(tīng)到內(nèi)部狀態(tài)的改變
})
xhr.addEventListener('load', function(){
//內(nèi)部數(shù)據(jù)到達(dá)會(huì)默認(rèn)觸發(fā)load
console.log(xhr.status);
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304){
//304 請(qǐng)求緩存
//成功了
var data = xhr.responseText;
console.log(data);
}else{
console.log('error');
}
xhr.onerror = function(){ //內(nèi)部出現(xiàn)錯(cuò)誤
console.log('error');//例如斷網(wǎng),數(shù)據(jù)還未到達(dá)服務(wù)器
}
xhr.ontimeout = function(){ //超時(shí)
}
}) //監(jiān)聽(tīng)
//status: 服務(wù)器的一個(gè)狀態(tài)赫蛇,數(shù)據(jù)是否OK
//readystate: 交互流程是否完畢绵患,0-4 五個(gè)狀態(tài)
//load: 狀態(tài)為4時(shí)會(huì)自動(dòng)觸發(fā)load,交互完畢一定是4悟耘,所以可以不寫(xiě)readystate
</script>
POST方法
<script>
var xhr = new XMLHttpRequest();
xhr.open('POST', '/login', true);
//把需要發(fā)送的數(shù)據(jù)拼好放在send中
xhr.send('username=zuodong&password=123');
//也可以將這個(gè)函數(shù)寫(xiě)進(jìn)send中落蝙,拼接URL
/*makeUrl({
username: 'zuodong',
age: 23
})*/
//需要發(fā)送的數(shù)據(jù),函數(shù)的方式
/*makeUrl({
username: 'zuodong',
age: 23
})*/
xhr.addEventListener('load', function(){
console.log(xhr.status);
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304){ //304 請(qǐng)求緩存
var data = xhr.responseText;
console.log(data);
}else{
console.log('error');
}
})
xhr.onerror = function(){ //內(nèi)部出現(xiàn)錯(cuò)誤
console.log('error');//例如斷網(wǎng)暂幼,數(shù)據(jù)還未到達(dá)服務(wù)器
}
makeUrl({
username: 'zuodong',
age: 23
})
function makeUrl(obj){
var arr = []
for(var key in obj){
arr.push( key + '=' + obj[key])
}
return arr.join('&')
}
//最終拼接成:username=zuodong&password=123
</script>
</body>
</html>
封裝AJAX
<script>
function ajax(opts){
var url = opts.url;
//假設(shè)用戶沒(méi)有設(shè)置type筏勒,則默認(rèn)為GET
var type = opts.type || 'GET';
//數(shù)據(jù)類型默認(rèn)為json
var dataType = opts.dataType || 'json';
//如果用戶沒(méi)有設(shè)置,則傳遞一個(gè)空函數(shù)
var onsuccess = opts.onsuccess || function(){};
var onerror = opts.onerror || function(){};
//如果沒(méi)有數(shù)據(jù)旺嬉,默認(rèn)傳遞一個(gè)空數(shù)據(jù)
var data = opts.data || {};
//將用戶傳遞的參數(shù)管行,如用戶名密碼,將這個(gè)對(duì)象進(jìn)行一個(gè)處理邪媳,序列化
var dataStr = [];
for(var key in data){
dataStr.push(key + '=' + data[key]);
}
dataStr = dataStr.join('&');
if(type === 'GET'){
url +='?' + dataStr;
}
var xhr = new XMLHttpRequest();
xhr.open(type, url, true);
xhr.onload = function(){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
//成功了
//如果用戶設(shè)置的需要返回的數(shù)據(jù)類型需要的是json格式
if(dataType === 'json'){
//把對(duì)象返回的數(shù)據(jù)解析成json格式
onsuccess(JSON.parse(xhr.responseText));
}else{
onsuccess(shr.responseText);
}
}else{
//如果是其他狀態(tài)捐顷,調(diào)用用戶的onerror
onerror();
}
}
xhr.onerror = onerror;
if(type === 'POST'){
xhr.send(dataStr);
}else{
xhr.send();
}
}
//約定好的需要傳遞的參數(shù)
ajax({
url: 'http://api.jirengu.com/weather.php',
data: {
city: '北京'
},
onsuccess: function(ret){
console.log(ret)
},
onerror: function(){
console.log('服務(wù)器異常');
}
})
</script>
XMLHttpRequest實(shí)例的屬性
readyState
只讀屬性,用一個(gè)正數(shù)對(duì)應(yīng)的常量雨效,表示XMLHttpRequest
請(qǐng)求當(dāng)前所處的狀態(tài)迅涮。
- 0:對(duì)用常量
UNSET
,表示XMLHttpRequest
實(shí)例已經(jīng)生成徽龟,但是open()
方法還沒(méi)有調(diào)用叮姑。 - 1:對(duì)應(yīng)常量
OPENED
,表示send()
方法還沒(méi)有被調(diào)用据悔,任然可以使用setRequestHeader()
传透,設(shè)定HTTP
請(qǐng)求頭信息。 - 2:對(duì)應(yīng)常量
HEADERS_RECEIVED
极颓,表示send()
方法已經(jīng)執(zhí)行朱盐,并且頭信息和狀態(tài)碼已收到。 - 3:對(duì)應(yīng)常量
LOADING
菠隆,表示正在接受服務(wù)器傳來(lái)的body
部分的數(shù)據(jù)兵琳,如果responseType
屬性是text
或者空字符串骚烧,responseText
就會(huì)包含已經(jīng)收到的部分信息。 - 4:對(duì)應(yīng)常量
DONE
闰围,表示服務(wù)器數(shù)據(jù)已經(jīng)完全接收,或者本次接收已經(jīng)失敗了既峡。
在通信過(guò)程中羡榴,每當(dāng)發(fā)生狀態(tài)變化的時(shí)候,readyState
屬性的值就會(huì)發(fā)生改變运敢。這個(gè)值每一次變化校仑,都會(huì)觸發(fā)readyStatyChange
事件。
responseType
responseType
屬性用來(lái)指定服務(wù)器返回?cái)?shù)據(jù)類型传惠。
"":字符串(默認(rèn)值)
arraybuffer:ArrayBuffer
對(duì)象
blob:Blob
對(duì)象
document:Document
對(duì)象
json:JSON
對(duì)象
text
:字符串
status
status
屬性為只讀屬性迄沫,表示本次請(qǐng)求所得到的HTTP
狀態(tài)碼,它是一個(gè)整數(shù)卦方。一般來(lái)說(shuō)羊瘩,如果通信成功的話,這個(gè)狀態(tài)碼是200盼砍。 - 200:OK尘吗,訪問(wèn)正常
- 301:Moved Permanently,永久移動(dòng)
- 302:Move temporarily浇坐,暫時(shí)移動(dòng)
- 304:Not Modified睬捶,未修改
- 307:Temporary Redirect,暫時(shí)重定向
- 401:Unauthorized近刘,未授權(quán)
- 403:Forbidden擒贸,禁止訪問(wèn)
- 404:Not Found,未發(fā)現(xiàn)指定網(wǎng)址
- 500:Internal Server Error觉渴,服務(wù)器發(fā)生錯(cuò)誤
基本只有2xx和304的狀態(tài)碼介劫,表示服務(wù)器返回是正常狀態(tài)。