Ajax工作原理及實例
1赔桌、關于ajax的名字
ajax 的全稱是Asynchronous JavaScript and XML沙咏,其中宴胧,Asynchronous 是異步的意思晶姊,它有別于傳統(tǒng)web開發(fā)中采用的同步的方式柬讨。
2崩瓤、關于同步和異步
步傳輸是面向字符的傳輸,它的單位是字符踩官;而同步傳輸是面向比特的傳輸却桶,它的單位是楨,它傳輸?shù)臅r候要求接受方和發(fā)送方的時鐘是保持一致的蔗牡。
具體來說肾扰,異步傳輸是將比特分成小組來進行傳送畴嘶。一般每個小組是一個8位字符,在每個小組的頭部和尾部都有一個開始位和一個停止位集晚,它在傳送過程中接收方和發(fā)送方的時鐘不要求一致窗悯,也就是說,發(fā)送方可以在任何時刻發(fā)送這些小組偷拔,而接收方并不知道它什么時候到達蒋院。一個最明顯的例子就是計算機鍵盤和主機的通信,按下一個鍵的同時向主機發(fā)送一個8比特位的ASCII代 碼莲绰,鍵盤可以在任何時刻發(fā)送代碼欺旧,這取決于用戶的輸入速度,內部的硬件必須能夠在任何時刻接收一個鍵入的字符蛤签。這是一個典型的異步傳輸過程辞友。異步傳輸存在 一個潛在的問題,即接收方并不知道數(shù)據(jù)會在什么時候到達震肮。在它檢測到數(shù)據(jù)并做出響應之前称龙,第一個比特已經過去了。這就像有人出乎意料地從后面走上來跟你說 話戳晌,而你沒來得及反應過來鲫尊,漏掉了最前面的幾個詞。因此沦偎,每次異步傳輸?shù)男畔⒍家砸粋€起始位開頭疫向,它通知接收方數(shù)據(jù)已經到達了,這就給了接收方響應豪嚎、接收 和緩存數(shù)據(jù)比特的時間搔驼;在傳輸結束時,一個停止位表示該次傳輸信息的終止侈询。按照慣例舌涨,空閑(沒有傳送數(shù)據(jù))的線路實際攜帶著一個代表二進制1的信號。步傳輸?shù)拈_始位使信號變成0妄荔,其他的比特位使信號隨傳輸?shù)臄?shù)據(jù)信息而變化泼菌。最后谍肤,停止位使信號重新變回1啦租,該信號一直保持到下一個開始位到達。例如在鍵盤上數(shù)字“1”荒揣,按照8比特位的擴展ASCII編碼篷角,將發(fā)送“00110001”,同時需要在8比特位的前面加一個起始位系任,后面一個停止位恳蹲。
同步傳輸?shù)谋忍胤纸M要大得多虐块。它不是獨立地發(fā)送每個字符,每個字符都有自己的開始位和停止位嘉蕾,而是把它們組合起來一起發(fā)送贺奠。我們將這些組合稱為數(shù)據(jù)幀,或簡稱為幀错忱。
數(shù)據(jù)幀的第一部分包含一組同步字符儡率,它是一個獨特的比特組合,類似于前面提到的起始位以清,用于通知接收方一個幀已經到達儿普,但它同時還能確保接收方的采樣速度和比特的到達速度保持一致,使收發(fā)雙方進入同步掷倔。
幀的最后一部分是一個幀結束標記眉孩。與同步字符一樣,它也是一個獨特的比特串勒葱,類似于前面提到的停止位浪汪,用于表示在下一幀開始之前沒有別的即將到達的數(shù)據(jù)了。
同步傳輸通常要比異步傳輸快速得多错森。接收方不必對每個字符進行開始和停止的操作吟宦。一旦檢測到幀同步字符,它就在接下來的數(shù)據(jù)到達時接收它們涩维。另外殃姓,同步傳輸?shù)拈_銷也比較少。例如瓦阐,一個典型的幀可能有500字節(jié)(即4000比特)的數(shù)據(jù)蜗侈,其中可能只包含100比特的開銷。這時睡蟋,增加的比特位使傳輸?shù)谋忍乜倲?shù)增加2.5%踏幻,這與異步傳輸中25 %的增值要小得多。隨著數(shù)據(jù)幀中實際數(shù)據(jù)比特位的增加戳杀,開銷比特所占的百分比將相應地減少该面。但是,數(shù)據(jù)比特位越長信卡,緩存數(shù)據(jù)所需要的緩沖區(qū)也越大隔缀,這就限制了一個幀的大小。另外傍菇,幀越大猾瘸,它占據(jù)傳輸媒體的連續(xù)時間也越長。在極端的情況下,這將導致其他用戶等得太久牵触。
3淮悼、ajax所包含的技術
ajax并非一種新的技術,而是幾種原有技術的結合體揽思。它由下列技術組合而成袜腥。
1.使用CSS和XHTML來表示。
- 使用DOM模型來交互和動態(tài)顯示钉汗。
3.使用XMLHttpRequest來和服務器進行異步通信瞧挤。
4.使用javascript來綁定和調用。
在上面幾中技術中儡湾,除了XmlHttpRequest對象以外特恬,其它所有的技術都是基于web標準并且已經得到了廣泛使用的,XMLHttpRequest雖然目前還沒有被W3C所采納徐钠,但是它已經是一個事實的標準癌刽,因為目前幾乎所有的主流瀏覽器都支持它。
** 4尝丐、ajax原理和XmlHttpRequest對象**
Ajax的原理簡單來說通過XmlHttpRequest對象來向服務器發(fā)異步請求显拜,從服務器獲得數(shù)據(jù),然后用javascript來操作DOM而更新頁面爹袁。這其中最關鍵的一步就是從服務器獲得請求數(shù)據(jù)远荠。要清楚這個過程和原理,我們必須對 XMLHttpRequest有所了解失息。
XMLHttpRequest是ajax的核心機制譬淳,它是在IE5中首先引入的,是一種支持異步請求的技術盹兢。簡單的說邻梆,也就是javascript可以及時向服務器提出請求和處理響應,而不阻塞用戶绎秒。達到無刷新的效果浦妄。
所以我們先從XMLHttpRequest講起,來看看它的工作原理见芹。
首先剂娄,我們先來看看XMLHttpRequest這個對象的屬性。
它的屬性有:
onreadystatechange 每次狀態(tài)改變所觸發(fā)事件的事件處理程序玄呛。
responseText 從服務器進程返回數(shù)據(jù)的字符串形式阅懦。
responseXML 從服務器進程返回的DOM兼容的文檔數(shù)據(jù)對象。
status 從服務器返回的數(shù)字代碼把鉴,比如常見的404(未找到)和200(已就緒)
status Text 伴隨狀態(tài)碼的字符串信息
readyState 對象狀態(tài)值
0 (未初始化) 對象已建立故黑,但是尚未初始化(尚未調用open方法)
1 (初始化) 對象已建立,尚未調用send方法
2 (發(fā)送數(shù)據(jù)) send方法已調用庭砍,但是當前的狀態(tài)及http頭未知
3 (數(shù)據(jù)傳送中) 已接收部分數(shù)據(jù)场晶,因為響應及http頭不全,這時通過responseBody和responseText獲取部分數(shù)據(jù)會出現(xiàn)錯誤怠缸,
4 (完成) 數(shù)據(jù)接收完畢,此時可以通過通過responseXml和responseText獲取完整的回應數(shù)據(jù)
但是诗轻,由于各瀏覽器之間存在差異,所以創(chuàng)建一個XMLHttpRequest對象可能需要不同的方法揭北。這個差異主要體現(xiàn)在IE和其它瀏覽器之間扳炬。下面是一個比較標準的創(chuàng)建XMLHttpRequest對象的方法。
[](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">function CreateXmlHttp() {
//非IE瀏覽器創(chuàng)建XmlHttpRequest對象
if (window.XmlHttpRequest) {
xmlhttp = new XmlHttpRequest();
}
//IE瀏覽器創(chuàng)建XmlHttpRequest對象
if (window.ActiveXObject) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
try {
xmlhttp = new ActiveXObject("msxml2.XMLHTTP");
}
catch (ex) { }
}
}
}
function Ustbwuyi() {
var data = document.getElementById("username").value;
CreateXmlHttp();
if (!xmlhttp) {
alert("創(chuàng)建xmlhttp對象異常搔体!");
return false;
}
xmlhttp.open("POST", url, false);
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
document.getElementById("user1").innerHTML = "數(shù)據(jù)正在加載...";
if (xmlhttp.status == 200) {
document.write(xmlhttp.responseText);
}
}
}
xmlhttp.send();
}</pre>
[](javascript:void(0); "復制代碼")
如上所示恨樟,函數(shù)首先檢查XMLHttpRequest的整體狀態(tài)并且保證它已經完成(readyStatus=4),即數(shù)據(jù)已經發(fā)送完畢疚俱。然后根據(jù)服務器的設定詢問請求狀態(tài)劝术,如果一切已經就緒(status=200),那么就執(zhí)行下面需要的操作呆奕。
對于XmlHttpRequest的兩個方法养晋,open和send,其中open方法指定了:
a梁钾、向服務器提交數(shù)據(jù)的類型绳泉,即post還是get。
b姆泻、請求的url地址和傳遞的參數(shù)零酪。
c、傳輸方式拇勃,false為同步蛾娶,true為異步。默認為true潜秋。如果是異步通信方式(true)蛔琅,客戶機就不等待服務器的響應;如果是同步方式(false)峻呛,客戶機就要等到服務器返回消息后才去執(zhí)行其他操作罗售。我們需要根據(jù)實際需要來指定同步方式,在某些頁面中钩述,可能會發(fā)出多個請求寨躁,甚至是有組織有計劃有隊形大規(guī)模的高強度的request,而后一個是會覆蓋前一個的牙勘,這個時候當然要指定同步方式职恳。
Send方法用來發(fā)送請求所禀。
知道了XMLHttpRequest的工作流程,我們可以看出放钦,XMLHttpRequest是完全用來向服務器發(fā)出一個請求的色徘,它的作用也局限于此,但它的作用是整個ajax實現(xiàn)的關鍵操禀,因為ajax無非是兩個過程褂策,發(fā)出請求和響應請求。并且它完全是一種客戶端的技術颓屑。而XMLHttpRequest正是處理了服務器端和客戶端通信的問題所以才會如此的重要斤寂。
現(xiàn)在,我們對ajax的原理大概可以有一個了解了揪惦。我們可以把服務器端看成一個數(shù)據(jù)接口遍搞,它返回的是一個純文本流,當然器腋,這個文本流可以是XML格式尾抑,可以是Html,可以是Javascript代碼蒂培,也可以只是一個字符串再愈。這時候,XMLHttpRequest向服務器端請求這個頁面护戳,服務器端將文本的結果寫入頁面翎冲,這和普通的web開發(fā)流程是一樣的,不同的是媳荒,客戶端在異步獲取這個結果后抗悍,不是直接顯示在頁面,而是先由javascript來處理钳枕,然后再顯示在頁面缴渊。至于現(xiàn)在流行的很多ajax控件,比如magicajax等鱼炒,可以返回DataSet等其它數(shù)據(jù)類型衔沼,只是將這個過程封裝了的結果,本質上他們并沒有什么太大的區(qū)別昔瞧。
5指蚁、ajax的缺點
下面我著重講一講ajax的缺陷,因為平時我們大多注意的都是ajax給我們所帶來的好處諸如用戶體驗的提升自晰。而對ajax所帶來的缺陷有所忽視凝化。
下面所闡述的ajax的缺陷都是它先天所產生的。
1酬荞、ajax干掉了back按鈕搓劫,即對瀏覽器后退機制的破壞瞧哟。后退按鈕是一個標準的web站點的重要功能,但是它沒法和js進行很好的合作枪向。這是ajax所帶來的一個比較嚴重的問題勤揩,因為用戶往往是希望能夠通過后退來取消前一次操作的。那么對于這個問題有沒有辦法遣疯?答案是肯定的,用過Gmail的知道凿傅,Gmail下面采用的ajax技術解決了這個問題缠犀,在Gmail下面是可以后退的,但是聪舒,它也并不能改變ajax的機制辨液,它只是采用的一個比較笨但是有效的辦法,即用戶單擊后退按鈕訪問歷史記錄時箱残,通過創(chuàng)建或使用一個隱藏的IFRAME來重現(xiàn)頁面上的變更滔迈。(例如,當用戶在Google Maps中單擊后退時被辑,它在一個隱藏的IFRAME中進行搜索燎悍,然后將搜索結果反映到Ajax元素上,以便將應用程序狀態(tài)恢復到當時的狀態(tài)盼理。)
但是谈山,雖然說這個問題是可以解決的,但是它所帶來的開發(fā)成本是非常高的宏怔,和ajax框架所要求的快速開發(fā)是相背離的奏路。這是ajax所帶來的一個非常嚴重的問題。
2臊诊、安全問題
技術同時也對IT企業(yè)帶來了新的安全威脅鸽粉,ajax技術就如同對企業(yè)數(shù)據(jù)建立了一個直接通道。這使得開發(fā)者在不經意間會暴露比以前更多的數(shù)據(jù)和服務器邏輯抓艳。ajax的邏輯可以對客戶端的安全掃描技術隱藏起來触机,允許黑客從遠端服務器上建立新的攻擊。還有ajax也難以避免一些已知的安全弱點玷或,諸如跨站點腳步攻擊威兜、SQL注入攻擊和基于credentials的安全漏洞等。
3庐椒、對搜索引擎的支持比較弱椒舵。
4、破壞了程序的異常機制约谈。至少從目前看來笔宿,像ajax.dll犁钟,ajaxpro.dll這些ajax框架是會破壞程序的異常機制的。關于這個問題泼橘,我曾經在開發(fā)過程中遇到過涝动,但是查了一下網上幾乎沒有相關的介紹。后來我自己做了一次試驗炬灭,分別采用ajax和傳統(tǒng)的form提交的模式來刪除一條數(shù)據(jù)……給我們的調試帶來了很大的困難醋粟。
5、另外重归,像其他方面的一些問題米愿,比如說違背了url和資源定位的初衷。例如鼻吮,我給你一個url地址育苟,如果采用了ajax技術,也許你在該url地址下面看到的和我在這個url地址下看到的內容是不同的椎木。這個和資源定位的初衷是相背離的违柏。
6、一些手持設備(如手機香椎、PDA等)現(xiàn)在還不能很好的支持ajax漱竖,比如說我們在手機的瀏覽器上打開采用ajax技術的網站時,它目前是不支持的畜伐,當然闲孤,這個問題和我們沒太多關系。
5烤礁、$.ajax()方法詳解
jquery中的ajax方法參數(shù)總是記不住讼积,這里記錄一下。
1.url:
要求為String類型的參數(shù)脚仔,(默認為當前頁地址)發(fā)送請求的地址勤众。
2.type:
要求為String類型的參數(shù),請求方式(post或get)默認為get鲤脏。注意其他http請求方法们颜,例如put和delete也可以使用,但僅部分瀏覽器支持猎醇。
3.timeout:
要求為Number類型的參數(shù)窥突,設置請求超時時間(毫秒)。此設置將覆蓋$.ajaxSetup()方法的全局設置硫嘶。
4.async:
要求為Boolean類型的參數(shù)阻问,默認設置為true,所有請求均為異步請求沦疾。如果需要發(fā)送同步請求称近,請將此選項設置為false第队。注意,同步請求將鎖住瀏覽器刨秆,用戶其他操作必須等待請求完成才可以執(zhí)行凳谦。
5.cache:
要求為Boolean類型的參數(shù),默認為true(當dataType為script時衡未,默認為false)尸执,設置為false將不會從瀏覽器緩存中加載請求信息。
6.data:
要求為Object或String類型的參數(shù)缓醋,發(fā)送到服務器的數(shù)據(jù)如失。如果已經不是字符串,將自動轉換為字符串格式改衩。get請求中將附加在url后岖常。防止這種自動轉換驯镊,可以查看 processData選項葫督。對象必須為key/value格式,例如{foo1:"bar1",foo2:"bar2"}轉換為&foo1=bar1&foo2=bar2板惑。如果是數(shù)組橄镜,JQuery將自動為不同值對應同一個名稱。例如{foo:["bar1","bar2"]}轉換為&foo=bar1&foo=bar2冯乘。
7.dataType:
要求為String類型的參數(shù)洽胶,預期服務器返回的數(shù)據(jù)類型。如果不指定裆馒,JQuery將自動根據(jù)http包mime信息返回responseXML或responseText姊氓,并作為回調函數(shù)參數(shù)傳遞∨绾茫可用的類型如下:
xml:返回XML文檔翔横,可用JQuery處理。
html:返回純文本HTML信息梗搅;包含的script標簽會在插入DOM時執(zhí)行禾唁。
script:返回純文本JavaScript代碼。不會自動緩存結果无切。除非設置了cache參數(shù)荡短。注意在遠程請求時(不在同一個域下),所有post請求都將轉為get請求哆键。
json:返回JSON數(shù)據(jù)掘托。
jsonp:JSONP格式。使用SONP形式調用函數(shù)時籍嘹,例如myurl?callback=?烫映,JQuery將自動替換后一個“?”為正確的函數(shù)名沼本,以執(zhí)行回調函數(shù)。
text:返回純文本字符串锭沟。
8.beforeSend:
要求為Function類型的參數(shù)抽兆,發(fā)送請求前可以修改XMLHttpRequest對象的函數(shù),例如添加自定義HTTP頭族淮。在beforeSend中如果返回false可以取消本次ajax請求辫红。XMLHttpRequest對象是惟一的參數(shù)。
function(XMLHttpRequest){
this; //調用本次ajax請求時傳遞的options參數(shù)
}
9.complete:
要求為Function類型的參數(shù)祝辣,請求完成后調用的回調函數(shù)(請求成功或失敗時均調用)贴妻。參數(shù):XMLHttpRequest對象和一個描述成功請求類型的字符串。
function(XMLHttpRequest, textStatus){
this; //調用本次ajax請求時傳遞的options參數(shù)
}
10.success:要求為Function類型的參數(shù)蝙斜,請求成功后調用的回調函數(shù)名惩,有兩個參數(shù)。
(1)由服務器返回孕荠,并根據(jù)dataType參數(shù)進行處理后的數(shù)據(jù)娩鹉。
(2)描述狀態(tài)的字符串。
function(data, textStatus){
//data可能是xmlDoc稚伍、jsonObj弯予、html、text等等
this; //調用本次ajax請求時傳遞的options參數(shù)
}
11.error:
要求為Function類型的參數(shù)个曙,請求失敗時被調用的函數(shù)锈嫩。該函數(shù)有3個參數(shù),即XMLHttpRequest對象垦搬、錯誤信息呼寸、捕獲的錯誤對象(可選)。ajax事件函數(shù)如下:
function(XMLHttpRequest, textStatus, errorThrown){
//通常情況下textStatus和errorThrown只有其中一個包含信息
this; //調用本次ajax請求時傳遞的options參數(shù)
}
12.contentType:
要求為String類型的參數(shù)猴贰,當發(fā)送信息至服務器時对雪,內容編碼類型默認為"application/x-www-form-urlencoded"。該默認值適合大多數(shù)應用場合糟趾。
13.dataFilter:
要求為Function類型的參數(shù)慌植,給Ajax返回的原始數(shù)據(jù)進行預處理的函數(shù)。提供data和type兩個參數(shù)义郑。data是Ajax返回的原始數(shù)據(jù)蝶柿,type是調用jQuery.ajax時提供的dataType參數(shù)。函數(shù)返回的值將由jQuery進一步處理非驮。
function(data, type){
//返回處理后的數(shù)據(jù)
return data;
}
14.dataFilter:
要求為Function類型的參數(shù)交汤,給Ajax返回的原始數(shù)據(jù)進行預處理的函數(shù)。提供data和type兩個參數(shù)。data是Ajax返回的原始數(shù)據(jù)芙扎,type是調用jQuery.ajax時提供的dataType參數(shù)星岗。函數(shù)返回的值將由jQuery進一步處理。
function(data, type){
//返回處理后的數(shù)據(jù)
return data;
}
15.global:
要求為Boolean類型的參數(shù)戒洼,默認為true俏橘。表示是否觸發(fā)全局ajax事件。設置為false將不會觸發(fā)全局ajax事件圈浇,ajaxStart或ajaxStop可用于控制各種ajax事件寥掐。
16.ifModified:
要求為Boolean類型的參數(shù),默認為false磷蜀。僅在服務器數(shù)據(jù)改變時獲取新數(shù)據(jù)召耘。服務器數(shù)據(jù)改變判斷的依據(jù)是Last-Modified頭信息。默認值是false褐隆,即忽略頭信息污它。
17.jsonp:
要求為String類型的參數(shù),在一個jsonp請求中重寫回調函數(shù)的名字庶弃。該值用來替代在"callback=?"這種GET或POST請求中URL參數(shù)里的"callback"部分衫贬,例如{jsonp:'onJsonPLoad'}會導致將"onJsonPLoad=?"傳給服務器。
18.username:
要求為String類型的參數(shù)虫埂,用于響應HTTP訪問認證請求的用戶名祥山。
19.password:
要求為String類型的參數(shù)圃验,用于響應HTTP訪問認證請求的密碼掉伏。
20.processData:
要求為Boolean類型的參數(shù),默認為true澳窑。默認情況下斧散,發(fā)送的數(shù)據(jù)將被轉換為對象(從技術角度來講并非字符串)以配合默認內容類型"application/x-www-form-urlencoded"。如果要發(fā)送DOM樹信息或者其他不希望轉換的信息摊聋,請設置為false鸡捐。
21.scriptCharset:
要求為String類型的參數(shù),只有當請求時dataType為"jsonp"或者"script"麻裁,并且type是GET時才會用于強制修改字符集(charset)箍镜。通常在本地和遠程的內容編碼不同時使用。
案例代碼:
[](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">('#send').click(function(){
("#username").val(), content:
('#resText').empty(); //清空resText里面的所有內容
var html = '';
('#resText').html(html);
}
});
});
});</pre>