前言
總括: 本文講解了ajax的歷史,工作原理以及優缺點,對XMLHttpRequest物件進行了詳細的講解,並使用原生js實現了一個ajax物件以方便日常開始使用。
- damonare的ajax庫:damonare的ajax庫
- 原文部落格地址:你真的懂ajax嗎?
- 知乎專欄&&簡書專題:前端進擊者(知乎)&&前端進擊者(簡書)
- 博主部落格地址:Damonare的個人部落格
古之立大事者,不惟有超世之才,亦必有堅忍不拔之志。
正文
相信每個前端程式設計師日常工作中都避免不了的工作就是和後端聯調,聯調自然就避免不了使用ajax
,但我相信,不管是使用jquery封裝的ajax
方法還是使用vue的外掛vue-resource
的程式設計師,真正對於ajax
有過深入探究的並不多,我們更多的是為了使用而使用,至於它的原理往往因為即使不瞭解依舊能做出東西而懶得去看,我們都被輪子們慣壞了。根據二八定律,即任何一組東西中,最重要的只佔其中一小部分,約20%,其餘80%的儘管是多數,卻是次要的。因 此,如果想擠進那20%的行列,就要學到一般人學不到的深度,學到一般人學不了的東西。
好的,現在我們從頭來說一下ajax
。
Ajax簡介
在上世紀90年代,幾乎所有的網站都由HTML頁面實現,伺服器處理每一個使用者請求都需要重新載入網頁。形式是怎樣的呢?就比如說你在瀏覽器上登入自己的微博賬號,填完了表單,點選登入按鈕,一次"完整"的HTTP請求就此觸發,伺服器發現你的登入密碼不對頭,立馬把網頁原原本本的返回給你,在使用者看來呢,就是一次重新載入的過程!使用者體驗極差!而且這個做法浪費了許多頻寬,因為在前後兩個頁面中的大部分HTML碼往往是相同的。由於每次應用的溝通都需要向伺服器傳送請求,應用的回應時間依賴於伺服器的回應時間。這導致了使用者介面的回應比本機應用慢得多。
到了2005年,google率先在它的應用(諸如google地圖、gmail)裡使用了ajax
技術,這才讓這項技術正式風靡開來。
如今它的應用已經十分廣泛:
- 運用XHTML+CSS來表達資訊;
- 運用JavaScript操作DOM(Document Object Model)來執行動態效果;
- 運用XML和XSLT運算元據;
- 運用XMLHttpRequest或新的Fetch API與網頁伺服器進行非同步資料交換;
- 注意:AJAX與Flash、Silverlight和Java Applet等RIA技術是有區分的。
Ajax工作原理
Ajax的工作原理相當於在使用者和伺服器之間加了一箇中間層(ajax引擎),使使用者操作與伺服器響應非同步化。並不是所有的使用者請求都提交給伺服器,像—些資料驗證(比如判斷使用者是否輸入了資料)和資料處理(比如判斷使用者輸入資料是否是數字)等都交給Ajax引擎自己來做, 只有確定需要從伺服器讀取新資料時再由Ajax引擎代為向伺服器提交請求。把這些交給了Ajax引擎,使用者操作起來也就感覺更加流暢了。
Ajax優缺點
Ajax的優點
1.無重新整理更新資料。
AJAX最大優點就是能在不重新整理整個頁面的前提下與伺服器通訊維護資料。這使得Web應用程式更為迅捷地響應使用者互動,並避免了在網路上傳送那些沒有改變的資訊,減少使用者等待時間,帶來非常好的使用者體驗。
2.非同步與伺服器通訊。
AJAX使用非同步方式與伺服器通訊,不需要打斷使用者的操作,具有更加迅速的響應能力。優化了Browser和Server之間的溝通,減少不必要的資料傳輸、時間及降低網路上資料流量。
3.前端和後端負載平衡。
AJAX可以把以前一些伺服器負擔的工作轉嫁到客戶端,利用客戶端閒置的能力來處理,減輕伺服器和頻寬的負擔,節約空間和寬頻租用成本。並且減輕伺服器的負擔,AJAX的原則是“按需取資料”,可以最大程度的減少冗餘請求和響應對伺服器造成的負擔,提升站點效能。
4.基於標準被廣泛支援。
AJAX基於標準化的並被廣泛支援的技術,不需要下載瀏覽器外掛或者小程式,但需要客戶允許JavaScript在瀏覽器上執行。隨著Ajax的成熟,一些簡化Ajax使用方法的程式庫也相繼問世。同樣,也出現了另一種輔助程式設計的技術,為那些不支援JavaScript的使用者提供替代功能。
5.介面與應用分離。
Ajax使WEB中的介面與應用分離(也可以說是資料與呈現分離),有利於分工合作、減少非技術人員對頁面的修改造成的WEB應用程式錯誤、提高效率、也更加適用於現在的釋出系統。
Ajax缺點
1.AjAX幹掉了Back和加入收藏書籤功能,即對瀏覽器機制的破壞。
對應用Ajax最主要的批評就是,它可能破壞瀏覽器的後退與加入收藏書籤功能。在動態更新頁面的情況下,使用者無法回到前一個頁面狀態,這是因為瀏覽器僅能記下歷史記錄中的靜態頁面。一個被完整讀入的頁面與一個已經被動態修改過的頁面之間的可能差別非常微妙;使用者通常都希望單擊後退按鈕,就能夠取消他們的前一次操作,但是在Ajax應用程式中,卻無法這樣做。不過開發者已想出了種種辦法來解決這個問題,HTML5 之前的方法大多是在使用者單擊後退按鈕訪問歷史記錄時,通過建立或使用一個隱藏的IFRAME來重現頁面上的變更。(例如,當使用者在Google Maps中單擊後退時,它在一個隱藏的IFRAME中進行搜尋,然後將搜尋結果反映到Ajax元素上,以便將應用程式狀態恢復到當時的狀態)。
關於無法將狀態加入收藏或書籤的問題,HTML5之前的一種方式是使用URL片斷識別符號(通常被稱為錨點,即URL中#後面的部分)來保持追蹤,允許使用者回到指定的某個應用程式狀態。(許多瀏覽器允許JavaScript動態更新錨點,這使得Ajax應用程式能夠在更新顯示內容的同時更新錨點。)HTML5 以後可以直接操作瀏覽歷史,並以字串形式儲存網頁狀態,將網頁加入網頁收藏夾或書籤時狀態會被隱形地保留。上述兩個方法也可以同時解決無法後退的問題。
2.AJAX的安全問題。
AJAX技術給使用者帶來很好的使用者體驗的同時也對IT企業帶來了新的安全威脅,Ajax技術就如同對企業資料建立了一個直接通道。這使得開發者在不經意間會暴露比以前更多的資料和伺服器邏輯。Ajax的邏輯可以對客戶端的安全掃描技術隱藏起來,允許黑客從遠端伺服器上建立新的攻擊。還有Ajax也難以避免一些已知的安全弱點,諸如跨站點腳步攻擊、SQL隱碼攻擊和基於Credentials的安全漏洞等等。
3.因為網路延遲需要給使用者提供必要提示
進行Ajax開發時,網路延遲——即使用者發出請求到伺服器發出響應之間的間隔——需要慎重考慮。如果不給予使用者明確的回應,沒有恰當的預讀資料,或者對XMLHttpRequest的不恰當處理,都會使使用者感到厭煩。通常的解決方案是,使用一個視覺化的元件來告訴使用者系統正在進行後臺操作並且正在讀取資料和內容。
XMLhttpRequest介紹
Ajax(Asynchronous JavaScript and XML)不是指一種單一的技術,而是有機地利用了一系列相關的技術。雖然其名稱包含XML,但實際上資料格式可以由JSON代替,進一步減少資料量,形成所謂的AJAJ。為了使用JavaScript向伺服器發出 HTTP 請求,需要一個提供此功能的類的例項。這就是XMLHttpRequest的由來。這樣的類最初是在Internet Explorer中作為一個名為XMLHTTP的ActiveX物件引入的。然後,Mozilla,Safari和其他瀏覽器,實現一個XMLHttpRequest類,支援Microsoft的原始ActiveX物件的方法和屬性。同時微軟也實現了XMLHttpRequest。
顯而易見XMLHttpRequest類是重中之重了。
XMLhttpRequest屬性
onreadystatechange
一個JavaScript函式物件,當readyState屬性改變時會呼叫它。回撥函式會在user interface執行緒中呼叫。
readyState
HTTP 請求的狀態.當一個 XMLHttpRequest 初次建立時,這個屬性的值從 0 開始,直到接收到完整的 HTTP 響應,這個值增加到 4。
5 個狀態中每一個都有一個相關聯的非正式的名稱,下表列出了狀態、名稱和含義:
狀態 | 名稱 | 描述 |
---|---|---|
0 | Uninitialized | 初始化狀態。XMLHttpRequest 物件已建立或已被 abort() 方法重置。 |
1 | Open | open() 方法已呼叫,但是 send() 方法未呼叫。請求還沒有被髮送。 |
2 | Sent | Send() 方法已呼叫,HTTP 請求已傳送到 Web 伺服器。未接收到響應。 |
3 | Receiving | 所有響應頭部都已經接收到。響應體開始接收但未完成。 |
4 | Loaded | HTTP 響應已經完全接收。 |
readyState 的值不會遞減,除非當一個請求在處理過程中的時候呼叫了 abort() 或 open() 方法。每次這個屬性的值增加的時候,都會觸發 onreadystatechange 事件控制程式碼。
responseText
目前為止為伺服器接收到的響應體(不包括頭部),或者如果還沒有接收到資料的話,就是空字串。
如果 readyState 小於 3,這個屬性就是一個空字串。當 readyState 為 3,這個屬性返回目前已經接收的響應部分。如果 readyState 為 4,這個屬性儲存了完整的響應體。
如果響應包含了為響應體指定字元編碼的頭部,就使用該編碼。否則,假定使用 Unicode UTF-8。
responseXML
對請求的響應,解析為 XML 並作為 Document 物件返回。
status
由伺服器返回的 HTTP 狀態程式碼,如 200 表示成功,而 404 表示 "Not Found" 錯誤。當 readyState 小於 3 的時候讀取這一屬性會導致一個異常。
statusText
這個屬性用名稱而不是數字指定了請求的 HTTP 的狀態程式碼。也就是說,當狀態為 200 的時候它是 "OK",當狀態為 404 的時候它是 "Not Found"。和 status 屬性一樣,當 readyState 小於 3 的時候讀取這一屬性會導致一個異常。
XMLHttpRequest方法
abort()
取消當前響應,關閉連線並且結束任何未決的網路活動。
這個方法把 XMLHttpRequest 物件重置為 readyState 為 0 的狀態,並且取消所有未決的網路活動。例如,如果請求用了太長時間,而且響應不再必要的時候,可以呼叫這個方法。
getAllResponseHeaders()
把 HTTP 響應頭部作為未解析的字串返回。
如果 readyState 小於 3,這個方法返回 null。否則,它返回伺服器傳送的所有 HTTP 響應的頭部。頭部作為單個的字串返回,一行一個頭部。每行用換行符 "\r\n" 隔開。
getResponseHeader()
返回指定的 HTTP 響應頭部的值。其引數是要返回的 HTTP 響應頭部的名稱。可以使用任何大小寫來制定這個頭部名字,和響應頭部的比較是不區分大小寫的。
該方法的返回值是指定的 HTTP 響應頭部的值,如果沒有接收到這個頭部或者 readyState 小於 3 則為空字串。如果接收到多個有指定名稱的頭部,這個頭部的值被連線起來並返回,使用逗號和空格分隔開各個頭部的值。
open()
初始化一個請求. 該方法用於JavaScript程式碼中;如果是原生程式碼, 使用 openRequest()
)方法代替.
注意: 在一個已經啟用的request下(已經呼叫open()或者openRequest()方法的request)再次呼叫這個方法相當於呼叫了abort()方法。
引數
method
請求所使用的HTTP方法; 例如 "GET", "POST", "PUT", "DELETE"等. 如果下個引數是非HTTP(S)的URL,則忽略該引數.
url
該請求所要訪問的URL
async
一個可選的布林值引數,預設為true,意味著是否執行非同步操作,如果值為false,則send()方法不會返回任何東西,直到接受到了伺服器的返回資料。如果為值為true,一個對開發者透明的通知會傳送到相關的事件監聽者。這個值必須是true,如果multipart 屬性是true,否則將會出現一個意外。
user
使用者名稱,可選引數,為授權使用;預設引數為空string.
password
密碼,可選引數,為授權使用;預設引數為空string.
send()
傳送 HTTP 請求,使用傳遞給 open() 方法的引數,以及傳遞給該方法的可選請求體。
setRequestHeader()
向一個開啟但未傳送的請求設定或新增一個 HTTP 請求(設定請求頭)。
引數
header
將要被賦值的請求頭名稱
value
給指定的請求頭賦的值
Ajax原生js實現
下面是使用原生js寫的ajax:
var ajax = {};
ajax.httpRequest = function () {
//判斷是否支援XMLHttpRequest物件
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
}
//相容IE瀏覽器
var versions = [
"MSXML2.XmlHttp.6.0",
"MSXML2.XmlHttp.5.0",
"MSXML2.XmlHttp.4.0",
"MSXML2.XmlHttp.3.0",
"MSXML2.XmlHttp.2.0",
"Microsoft.XmlHttp"
];
//定義區域性變數xhr,儲存IE瀏覽器的ActiveXObject物件
var xhr;
for (var i = 0; i < versions.length; i++) {
try {
xhr = new ActiveXObject(versions[i]);
break;
} catch (e) {
}
}
return xhr;
};
ajax.send = function (url, callback, method, data, async) {
//預設非同步
if (async === undefined) {
async = true;
}
var httpRequest = ajax.httpRequest();
//初始化HTTP請求
httpRequest.open(method, url, async);
//onreadystatechange函式物件
httpRequest.onreadystatechange = function () {
//readyState 的值等於4,從伺服器拿到了資料
if (httpRequest.readyState == 4) {
//回撥伺服器響應資料
callback(httpRequest.responseText)
}
};
if (method == 'POST') {
//給指定的HTTP請求頭賦值
httpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
}
//傳送HTTP請求
httpRequest.send(data);
};
//實現GET請求
ajax.get = function (url, data, callback, async) {
var query = [];
for (var key in data) {
query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
}
ajax.send(url + (query.length ? '?' + query.join('&') : ''), callback, 'GET', null, async)
};
//實現POST請求
ajax.post = function (url, data, callback, async) {
var query = [];
for (var key in data) {
query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
}
ajax.send(url, callback, 'POST', query.join('&'), async)
};複製程式碼
如果你使用jquery或是zepto很大部分是因為它的ajax相容性高的緣故,不妨試試這個:damonare的ajax庫,喜歡給個star也是可以的。
後記
ajax技術對於整個web應用意義都是非凡的,僅以此篇致敬那些曾經奮鬥在一線為了ajax技術的實現和普及做出工作的前輩們。