我們平常工作中經(jīng)常會(huì)用到ajax向服務(wù)器發(fā)送請(qǐng)求,今天就來詳細(xì)的來了解一下ajax的前世與今生!
簡(jiǎn)介
瀏覽器與服務(wù)器之間,采用HTTP 協(xié)議通信。用戶在瀏覽器地址欄鍵入一個(gè)網(wǎng)址,或者通過網(wǎng)頁表單向服務(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í)并沒有引起注意,直到2004年 Gmail 發(fā)布和2005年 Google Map 發(fā)布,才引起廣泛重視。2005年2月,AJAX 這個(gè)詞第一次正式提出,它是 Asynchronous JavaScript and XML 的縮寫,指的是通過 JavaScript 的異步通信,從服務(wù)器獲取 XML 文檔從中提取數(shù)據(jù),再更新當(dāng)前網(wǎng)頁的對(duì)應(yīng)部分,而不用刷新整個(gè)網(wǎng)頁。后來,AJAX這個(gè)詞就成為JavaScript 腳本發(fā)起 HTTP 通信的代名詞,也就是說,只要用腳本發(fā)起通信,就可以叫做 AJAX 通信。W3C 也在2006年發(fā)布了它的國際標(biāo)準(zhǔn)。
AJAX 包括以下幾個(gè)步驟。
- 創(chuàng)建XMLHttpRequest
- 發(fā)出 HTTP請(qǐng)求
- 接受服務(wù)器傳回的數(shù)據(jù)
- 更新網(wǎng)頁數(shù)據(jù)
那么什么是ajax呢
就是一句話,AJAX通過原生的XMLHttpRequest對(duì)象發(fā)出 HTTP 請(qǐng)求,得到服務(wù)器返回的數(shù)據(jù)后,再進(jìn)行處理?,F(xiàn)在,服務(wù)器返回的都是 JSON格式的數(shù)據(jù),XML 格式已經(jīng)過時(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)。
所以我用一句話來總結(jié)兩者的關(guān)系:
我們使用XMLHttpRequest對(duì)象來發(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)聽
</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à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),握手的過程,每次握手會(huì)發(fā)生改變,可以監(jiān)聽到內(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)聽
//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,所以可以不寫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ù)寫進(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è)用戶沒有設(shè)置type,則默認(rèn)為GET
var type = opts.type || 'GET';
//數(shù)據(jù)類型默認(rèn)為json
var dataType = opts.dataType || 'json';
//如果用戶沒有設(shè)置,則傳遞一個(gè)空函數(shù)
var onsuccess = opts.onsuccess || function(){};
var onerror = opts.onerror || function(){};
//如果沒有數(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()方法還沒有調(diào)用。 - 1:對(duì)應(yīng)常量
OPENED,表示send()方法還沒有被調(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ù)器傳來的body部分的數(shù)據(jù),如果responseType屬性是text或者空字符串,responseText就會(huì)包含已經(jīng)收到的部分信息。 - 4:對(duì)應(yīng)常量
DONE,表示服務(wù)器數(shù)據(jù)已經(jīng)完全接收,或者本次接收已經(jīng)失敗了。
在通信過程中,每當(dāng)發(fā)生狀態(tài)變化的時(shí)候,readyState屬性的值就會(huì)發(fā)生改變。這個(gè)值每一次變化,都會(huì)觸發(fā)readyStatyChange事件。
responseType
responseType屬性用來指定服務(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ù)。一般來說,如果通信成功的話,這個(gè)狀態(tài)碼是200。 - 200:OK,訪問正常
- 301:Moved Permanently,永久移動(dòng)
- 302:Move temporarily,暫時(shí)移動(dòng)
- 304:Not Modified,未修改
- 307:Temporary Redirect,暫時(shí)重定向
- 401:Unauthorized,未授權(quán)
- 403:Forbidden,禁止訪問
- 404:Not Found,未發(fā)現(xiàn)指定網(wǎng)址
- 500:Internal Server Error,服務(wù)器發(fā)生錯(cuò)誤
基本只有2xx和304的狀態(tài)碼,表示服務(wù)器返回是正常狀態(tài)。