引語(yǔ)AJAX 即 Asynchronous Javascript And XML(異步JavaScript和XML),是指一種創(chuàng)建交互式網(wǎng)頁(yè)應(yīng)用的網(wǎng)頁(yè)開(kāi)發(fā)技術(shù)。
AJAX 是一種用于創(chuàng)建快速動(dòng)態(tài)網(wǎng)頁(yè)的技術(shù)。它可以令開(kāi)發(fā)者只向服務(wù)器獲取數(shù)據(jù)(而不是圖片,HTML文檔等資源),互聯(lián)網(wǎng)資源的傳輸變得前所未有的輕量級(jí)和純粹,這激發(fā)了廣大開(kāi)發(fā)者的創(chuàng)造力,使各式各樣功能強(qiáng)大的網(wǎng)絡(luò)站點(diǎn),和互聯(lián)網(wǎng)應(yīng)用如雨后春筍一般冒出,不斷帶給人驚喜。

一、什么是AJAXAjax是一種異步請(qǐng)求數(shù)據(jù)的web開(kāi)發(fā)技術(shù),對(duì)于改善用戶(hù)的體驗(yàn)和頁(yè)面性能很有幫助。簡(jiǎn)單地說(shuō),在不需要重新刷新頁(yè)面的情況下,Ajax 通過(guò)異步請(qǐng)求加載后臺(tái)數(shù)據(jù),并在網(wǎng)頁(yè)上呈現(xiàn)出來(lái)。常見(jiàn)運(yùn)用場(chǎng)景有表單驗(yàn)證是否登入成功、百度搜索下拉框提示和快遞單號(hào)查詢(xún)等等。
Ajax的目的是提高用戶(hù)體驗(yàn),較少網(wǎng)絡(luò)數(shù)據(jù)的傳輸量。同時(shí),由于AJAX請(qǐng)求獲取的是數(shù)據(jù)而不是HTML文檔,因此它也節(jié)省了網(wǎng)絡(luò)帶寬,讓互聯(lián)網(wǎng)用戶(hù)的網(wǎng)絡(luò)沖浪體驗(yàn)變得更加順暢。 二、AJAX原理是什么Ajax相當(dāng)于在用戶(hù)和服務(wù)器之間加了一個(gè)中間層,使用戶(hù)操作與服務(wù)器響應(yīng)異步化。并不是所有的用戶(hù)請(qǐng)求都提交給服務(wù)器,像一些數(shù)據(jù)驗(yàn)證和數(shù)據(jù)處理等都交給Ajax引擎自己來(lái)做,只有確定需要從服務(wù)器讀取新數(shù)據(jù)時(shí)再由Ajax引擎代為向服務(wù)器提交請(qǐng)求。 Ajax的原理簡(jiǎn)單來(lái)說(shuō)通過(guò)XmlHttpRequest對(duì)象來(lái)向服務(wù)器發(fā)送異步請(qǐng)求,從服務(wù)器獲得數(shù)據(jù),然后用JavaScript來(lái)操作DOM而更新頁(yè)面。這其中最關(guān)鍵的一步就是從服務(wù)器獲得請(qǐng)求數(shù)據(jù)。要清楚這個(gè)過(guò)程和原理,我們必須對(duì) XMLHttpRequest有所了解。 XMLHttpRequest是ajax的核心機(jī)制,它是在IE5中首先引入的,是一種支持異步請(qǐng)求的技術(shù)。簡(jiǎn)單的說(shuō),也就是JavaScript可以及時(shí)向服務(wù)器提出請(qǐng)求和處理響應(yīng),而不阻塞用戶(hù)。達(dá)到無(wú)刷新的效果。 三、AJAX的使用1. 創(chuàng)建Ajax核心對(duì)象XMLHttpRequest(記得考慮兼容性)let xhr = null;
if (window.`XMLHttpRequest`) {// 兼容 IE7+, Firefox, Chrome, Opera, Safari
xhr = new `XMLHttpRequest`();
} else {// 兼容 IE6, IE5
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
2. 向服務(wù)器發(fā)送請(qǐng)求xhr.open(method, url, async);
send(string);//`POST`請(qǐng)求時(shí)才使用字符串參數(shù),否則不用帶參數(shù)。
注意:POST請(qǐng)求一定要設(shè)置請(qǐng)求頭的格式內(nèi)容 xhr.open("`POST`", "test.html", true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send("fname=Henry&lname=Ford"); //`POST`請(qǐng)求參數(shù)放在send里面,即請(qǐng)求體
一個(gè)Promise對(duì)象實(shí)現(xiàn)的 Ajax 操作的例子: const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出錯(cuò)了', error);
});
3. 服務(wù)器響應(yīng)處理(區(qū)分同步跟異步兩種情況)responseText 獲得字符串形式的響應(yīng)數(shù)據(jù)。
responseXML 獲得XML 形式的響應(yīng)數(shù)據(jù)。
同步處理xhr.open("`GET`","info.txt",false);
xhr.send();
document.`GET`ElementById("myDiv").innerHTML = xhr.responseText; //獲取數(shù)據(jù)直接顯示在頁(yè)面上
異步處理(推薦)相對(duì)來(lái)說(shuō)比較復(fù)雜,要在請(qǐng)求狀態(tài)改變事件中處理。 xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200){
document.`GET`ElementById("myDiv").innerHTML = xhr.responseText;
}
}
什么是readyState?readyState是XMLHttpRequest對(duì)象的一個(gè)屬性,用來(lái)標(biāo)識(shí)當(dāng)前XMLHttpRequest對(duì)象處于什么狀態(tài)。 readyState總共有5個(gè)狀態(tài)值,分別為0~4,每個(gè)值代表了不同的含義:
0:未初始化 — 尚未調(diào)用.open()方法;
1:?jiǎn)?dòng) — 已經(jīng)調(diào)用.open()方法,但尚未調(diào)用.send()方法;
2:發(fā)送 — 已經(jīng)調(diào)用.send()方法,但尚未接收到響應(yīng);
3:接收 — 已經(jīng)接收到部分響應(yīng)數(shù)據(jù);
4:完成 — 已經(jīng)接收到全部響應(yīng)數(shù)據(jù),而且已經(jīng)可以在客戶(hù)端使用了;
什么是status?HTTP狀態(tài)碼(status)由三個(gè)十進(jìn)制數(shù)字組成,第一個(gè)十進(jìn)制數(shù)字定義了狀態(tài)碼的類(lèi)型,后兩個(gè)數(shù)字沒(méi)有分類(lèi)的作用。HTTP狀態(tài)碼共分為5種類(lèi)型: 1xx(臨時(shí)響應(yīng)):表示臨時(shí)響應(yīng)并需要請(qǐng)求者繼續(xù)執(zhí)行操作的狀態(tài)碼。
2xx(成功):表示成功處理了請(qǐng)求的狀態(tài)碼。
3xx(重定向):表示要完成請(qǐng)求,需要進(jìn)一步操作。通常,這些狀態(tài)代碼用來(lái)重定向。
4xx(請(qǐng)求錯(cuò)誤):這些狀態(tài)碼表示請(qǐng)求可能出錯(cuò),妨礙了服務(wù)器的處理。
5xx(服務(wù)器錯(cuò)誤):這些狀態(tài)碼表示服務(wù)器在嘗試處理請(qǐng)求時(shí)發(fā)生內(nèi)部錯(cuò)誤。這些錯(cuò)誤可能是服務(wù)器本身的錯(cuò)誤,而不是請(qǐng)求出錯(cuò)。
常見(jiàn)的狀態(tài)碼僅記錄在 RFC2616 上的 HTTP 狀態(tài)碼就達(dá) 40 種,若再加上 WebDAV(RFC4918、5842)和附加 HTTP 狀態(tài)碼 (RFC6585)等擴(kuò)展,數(shù)量就達(dá) 60 余種。接下來(lái),我們就介紹一下這些具有代表性的一些狀態(tài)碼。 200 表示從客戶(hù)端發(fā)來(lái)的請(qǐng)求在服務(wù)器端被正常處理了。
204 表示請(qǐng)求處理成功,但沒(méi)有資源返回。
301 表示永久性重定向。該狀態(tài)碼表示請(qǐng)求的資源已被分配了新的URI,以后應(yīng)使用資源現(xiàn)在所指的URI。
302 表示臨時(shí)性重定向。
304 表示客戶(hù)端發(fā)送附帶條件的請(qǐng)求時(shí)(指采用GET方法的請(qǐng)求報(bào)文中包含if-matched,if-modified-since,if-none-match,if-range,if-unmodified-since任一個(gè)首部)服務(wù)器端允許請(qǐng)求訪(fǎng)問(wèn)資源,但因發(fā)生請(qǐng)求未滿(mǎn)足條件的情況后,直接返回304Modified(服務(wù)器端資源未改變,可直接使用客戶(hù)端未過(guò)期的緩存)
400 表示請(qǐng)求報(bào)文中存在語(yǔ)法錯(cuò)誤。當(dāng)錯(cuò)誤發(fā)生時(shí),需修改請(qǐng)求的內(nèi)容后再次發(fā)送請(qǐng)求。
401 表示未授權(quán)(Unauthorized),當(dāng)前請(qǐng)求需要用戶(hù)驗(yàn)證
403 表示對(duì)請(qǐng)求資源的訪(fǎng)問(wèn)被服務(wù)器拒絕了
404 表示服務(wù)器上無(wú)法找到請(qǐng)求的資源。除此之外,也可以在服務(wù)器端拒絕請(qǐng)求且不想說(shuō)明理由時(shí)使用。
500 表示服務(wù)器端在執(zhí)行請(qǐng)求時(shí)發(fā)生了錯(cuò)誤。也有可能是Web應(yīng)用存在的bug或某些臨時(shí)的故障。
503 表示服務(wù)器暫時(shí)處于超負(fù)載或正在進(jìn)行停機(jī)維護(hù),現(xiàn)在無(wú)法處理請(qǐng)求。
4. GET和POST請(qǐng)求數(shù)據(jù)區(qū)別GET在瀏覽器回退時(shí)是無(wú)害的,而POST會(huì)再次提交請(qǐng)求。
GET產(chǎn)生的URL地址可以被Bookmark,而POST不可以。
GET請(qǐng)求會(huì)被瀏覽器主動(dòng)cache,而POST不會(huì),除非手動(dòng)設(shè)置。
GET請(qǐng)求只能進(jìn)行url編碼,而POST支持多種編碼方式。
GET請(qǐng)求參數(shù)會(huì)被完整保留在瀏覽器歷史記錄里,而POST中的參數(shù)不會(huì)被保留。
GET請(qǐng)求在URL中傳送的參數(shù)是有長(zhǎng)度限制的,而POST么有。
對(duì)參數(shù)的數(shù)據(jù)類(lèi)型,GET只接受ASCII字符,而POST沒(méi)有限制。 GET比POST更不安全,因?yàn)閰?shù)直接暴露在URL上,所以不能用來(lái)傳遞敏感信息。
GET參數(shù)通過(guò)URL傳遞,POST放在Request body中。
GET和POST使用場(chǎng)景: 若符合下列任一情況,則推薦用POST方法:
請(qǐng)求的結(jié)果有持續(xù)性的副作用,例如,數(shù)據(jù)庫(kù)內(nèi)添加新的數(shù)據(jù)行。 若使用GET方法,則表單上收集的數(shù)據(jù)可能讓URL過(guò)長(zhǎng)。 要傳送的數(shù)據(jù)不是采用7位的ASCII編碼。
若符合下列任一情況,則推薦用GET方法: 請(qǐng)求是為了查找資源,HTML表單數(shù)據(jù)僅用來(lái)幫助搜索。 請(qǐng)求結(jié)果無(wú)持續(xù)性的副作用。 收集的數(shù)據(jù)及HTML表單內(nèi)的輸入字段名稱(chēng)的總長(zhǎng)不超過(guò)1024個(gè)字符。
四、常見(jiàn)AJAX面試題什么是AJAX?AJAX作用是什么?詳見(jiàn)本文內(nèi)容=> 原生JavaScript AJAX請(qǐng)求有幾個(gè)步驟?分別是什么?//創(chuàng)建 XMLHttpRequest 對(duì)象
var xhr = new XMLHttpRequest();
//發(fā)送信息至服務(wù)器時(shí)內(nèi)容編碼類(lèi)型
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//接受服務(wù)器響應(yīng)數(shù)據(jù)
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && (xhr.status == 200) {
// let data = xhr.responseText;
}
};
//規(guī)定請(qǐng)求的類(lèi)型、URL 以及是否異步處理請(qǐng)求。
xhr.open('GET',url,true);
//發(fā)送請(qǐng)求
xhr.send(null);
JSON字符串和JSON對(duì)象的相互轉(zhuǎn)換//字符串轉(zhuǎn)對(duì)象
JSON.parse(json)
eval('(' + jsonstr + ')')
// 對(duì)象轉(zhuǎn)字符串
JSON.stringify(json)
AJAX幾種請(qǐng)求方式?他們的優(yōu)缺點(diǎn)?詳見(jiàn)本文內(nèi)容=> HTTP常見(jiàn)狀態(tài)碼有哪些?詳見(jiàn)本文內(nèi)容=> 什么情況造成跨域(什么是同源策略)?同源策略是瀏覽器的一個(gè)安全功能,不同源的客戶(hù)端腳本在沒(méi)有明確授權(quán)的情況下,不能讀寫(xiě)對(duì)方資源。所以xyz.com下的js腳本采用Ajax讀取abc.com里面的文件數(shù)據(jù)是會(huì)被拒絕的。 同源策略限制了從同一個(gè)源加載的文檔或腳本如何與來(lái)自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的重要安全機(jī)制。 舉個(gè)例子: 
不受同源策略限制的情況: 頁(yè)面中的鏈接,重定向以及表單提交是不會(huì)受到同源策略限制的。 跨域資源的引入是可以的。但是js不能讀寫(xiě)加載的內(nèi)容。如嵌入到頁(yè)面中的<script src="..."></script>,<img>,<link>,<iframe>等。
跨域解決方案有哪些?JSONP 只能解決GET跨域(問(wèn)的最多) 原理:動(dòng)態(tài)創(chuàng)建一個(gè)script標(biāo)簽。利用script標(biāo)簽的src屬性不受同源策略限制。因?yàn)樗械膕rc屬性和href屬性都不受同源策略限制??梢哉?qǐng)求第三方服務(wù)器數(shù)據(jù)內(nèi)容。 步驟: 1. 創(chuàng)建一個(gè)script標(biāo)簽
2. script的src屬性設(shè)置接口地址
3. 接口參數(shù),必須要帶一個(gè)自定義函數(shù)名 要不然后臺(tái)無(wú)法返回?cái)?shù)據(jù)。
4. 通過(guò)定義函數(shù)名去接收后臺(tái)返回?cái)?shù)據(jù)
```js
//去創(chuàng)建一個(gè)script標(biāo)簽
let script = document.createElement("script");
//script的src屬性設(shè)置接口地址 并帶一個(gè)callback回調(diào)函數(shù)名稱(chēng)
script.src = "http://127.0.0.1:8888/index.php?callback=jsonpCallback";
//插入到頁(yè)面
document.head.appendChild(script);
//通過(guò)定義函數(shù)名去接收后臺(tái)返回?cái)?shù)據(jù)
function jsonpCallback(data){
//注意:jsonp返回的數(shù)據(jù)是json對(duì)象可以直接使用
//ajax 取得數(shù)據(jù)是json字符串需要轉(zhuǎn)換成json對(duì)象才可以使用。
}
```
CORS:跨域資源共享 原理:服務(wù)器設(shè)置Access-Control-Allow-OriginHTTP響應(yīng)頭之后,瀏覽器將會(huì)允許跨域請(qǐng)求 限制:瀏覽器需要支持HTML5,可以支持POST,PUT等方法兼容ie9以上 需要后臺(tái)設(shè)置 Access-Control-Allow-Origin: * //允許所有域名訪(fǎng)問(wèn),或者
Access-Control-Allow-Origin: http:// //只允許所有域名訪(fǎng)問(wèn)
設(shè)置 document.domain 原理:相同主域名不同子域名下的頁(yè)面,可以設(shè)置document.domain讓它們同域 限制:同域document提供的是頁(yè)面間的互操作,需要載入iframe頁(yè)面 // URL http:///foo
var ifr = document.createElement('iframe');
ifr.src = 'http://b./bar';
ifr.onload = function(){
var ifrdoc = ifr.contentDocument || ifr.contentWindow.document;
ifrdoc.getElementsById("foo").innerHTML);
};
ifr.style.display = 'none';
document.body.appendChild(ifr);
ES5 postMessage ES5新增的 postMessage() 方法允許來(lái)自不同源的腳本采用異步方式進(jìn)行有限的通信,可以實(shí)現(xiàn)跨文本檔、多窗口、跨域消息傳遞. 語(yǔ)法: postMessage(data,origin)
用Apache做轉(zhuǎn)發(fā)(逆向代理),讓跨域變成同域
五、結(jié)語(yǔ)其實(shí)通過(guò) XMLHttpRequest或者封裝后的框架進(jìn)行網(wǎng)絡(luò)請(qǐng)求,這種方式已經(jīng)有點(diǎn)老舊了,配置和調(diào)用方式非?;靵y,近幾年剛剛出來(lái)的Fetch提供了一個(gè)更好的替代方法,它不僅提供了一種簡(jiǎn)單,合乎邏輯的方式來(lái)跨網(wǎng)絡(luò)異步獲取資源,而且可以很容易地被其他技術(shù)使用。
|