同步與非同步
同步HTTP請求:請求 → 顯示 → 互動 → 暫停 → 請求 → 顯示 → 互動 → 暫停 …需要不斷重新刷新整個頁面,會出現一段時間的空白。
非同步HTTP請求:請求 → 顯示 → 互動 → (請求)互動 → (請求)互動 …
非同步HTTP請求在顯示、執行互動不需要再暫停操作,可以馬上執行所需的互動,只向伺服器要求一小部份資料,然後更新頁面的一小部份, 不需要重新刷新整個頁面,也不會出現一段時間的空白。
非同步好處
- 減輕伺服器的負擔,加快瀏覽速度(只給所需)
- 帶來更好的用戶體驗(更流暢的畫面)
- 標準化技術,主流瀏覽器都支援
- 促進頁面呈現與資料分離(提高可維護性)
AJAX組成
AJAX不是單一技術,是集4種技術而成,JavaScript、CSS、DOM、XMLHttpRequest。(或說5種,再加上(X)HTML)名稱 | 說明 |
---|---|
JavaScript | 通用指令碼語言。是撰寫AJAX主要程式語言 |
CSS | 為Web頁面元素提供視覺化樣式的定義方法, AJAX透過修改CSS樣式,改變使用者介面 |
DOM | AJAX可以在執行時透過DOM來改變使用者介面,或局部更新某個節點 |
XMLHttpRequest | XMLHttpRequest物件允許Web開發人員向伺服器以非同步方式取資料, 資料格式通常是XML或是文字,目前較流行為JSON |
XMLHttpRequest物件
能夠直接在Client使用Javascript來提出HTTP請求。IE內建MSXML剖析器有多個版本,其提供的XMLHttpReqest物件也有多個版本(不建議使用)。KKBruce提醒,除非您必須處理類似IE等相容性問題,不然是不建議使用以下表格內容。
版本 | DLL名稱 | ProgID |
---|---|---|
2.0(IE5) | msxml.dll | Microsoft.XMLHttp |
2.6 | msxml2.dll | MSXML2.XMLHttp |
3.0(IE6) | msxml3.dll | MSXML2.XMLHttp.3.0 |
4.0 | msxml4.dll | MSXML2.XMLHttp.4.0 |
5.0 | msxml5.dll | MSXML2.XMLHttp.5.0 |
6.0 | msxml6.dll | MSXML2.XMLHttp.6.0 |
建立XMLHttpRequest物件
在Mozilla,Chrome和Safaris等主流瀏覽器是內建物件,例如,// 1. 透過 new XMLHttpRequest() 產生物件 // 2. 注意大小寫 var xmlHttp=new XMLHttpRequest();
在IE6之前是透過ActiveX控制項物件(由MSXML提供),例如,
var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
IE7之後,同時支援原生XMLHttpRequest物件(預設值)和ActiveX控制項物件,例如,
var xmlHttp; function createXMLHttpRequest(){ if (window.XMLHttpRequest) //支援原生型 xmlHttp = new XMLHttpRequest(); else if (window.ActiveXObject) //IE6以下 xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); }
[KKBruce補充] 建立XMLHttpRequest物件注意事項
XMLHttpRequest物件屬性
名稱 | 說明 |
---|---|
readyState | 唯讀屬性,目前狀態 |
responseBody | 唯讀屬性,取得伺服器的回應,傳回非符號位元組的陣列 |
responseSteam | 唯讀屬性,取得伺服器的回應,傳回IStream資料流物件 |
responseText | 唯讀屬性,取得伺服器的回應,傳回字串 |
responseXML | 唯讀屬性,取得伺服器的回應,傳回XML DOM物件,可作DOM處理 |
status | 唯讀屬性,取得HTTP狀態碼 |
statusText | 唯讀屬性,取得HTTP狀態訊息字串 |
onreadystatechange | 存取屬性,執行readyState屬性改變的事件處理,設定處理的程序 |
readyState會回傳一數值,以表示目前處理的進度。
名稱 | 說明 |
---|---|
0 | uninitalized,未初始化 |
1 | loading,載入中 |
2 | loaded,載入完成 |
3 | interactive,可互動 |
4 | completed,完成 |
status會回傳狀態碼,以表示目前處理的結果。
名稱 | 說明 |
---|---|
200 | OK,請求成功 |
202 | 請求被接收,但處理未完成 |
400 | 錯誤的請求 |
404 | Not Found,找不到網頁 |
500 | Internal Server Error,內部錯誤 |
... | …其他網頁錯誤碼 |
[KKBruce補充] 這裡有一篇整理很棒的 HTTP 狀態碼,【HTTP 狀態碼,在 IIS 7.0 和 IIS 7.5】非常建議加入我的最愛中,在查詢錯誤的 HTTP 狀態碼時非常有用。例如,我們一般只知道 404 狀態碼是找不到,但 404 下還有整整 20 項細部的定義,這對於開發人員是不可多得的資訊,可以更快速清楚問題所在,而不是用經驗法則在處理。
XMLHttpRequest物件方法
名稱 | 說明 |
---|---|
abort() | 取消目前HTTP請求 |
getAllResponseHeaders() | 取得全部HTTP標頭內容 |
getResponseHeader("指定Header名稱") | 取得指定HTTP標頭名稱的內容 |
open("GET|POST","URL",async,userid,pwd) | 開啟HTTP請求 |
send("參數") | 傳送HTTP請求到伺服器 |
setRequestHeader("自訂Header名稱",value) | 自訂HTTP標頭資料 |
名稱 | 說明 |
---|---|
GET | 取得伺服器回應 |
POST | 傳送資料到伺服器 |
URL | 請求的XML文件或伺服端程式(ASP.NET, PHP, JSP...) |
async | 布林值, 預設true,false表示需要等到伺服器回應後才繼續執行Javascript |
userid, pwd | 伺服器需要id/password,才需要使用 |
send("參數")說明
參數表示要向伺服器發送的資料,參數格式為查詢字串的形式,例如,var param = "name=Bruce&age=18";在open中指定為GET,則參數作為查詢字串傳送,如果指定為POST,則作為HTTP的POST方法傳送,對於send()方法而言,此參數為必需, 如不發送任何資料,請傳送null即可。
// GET xmlHttp.send(null); // POST xmlHttp.send(param);
setRequestHeader()說明
如果使用POST方法,在發送之前必須設定HTTP標頭,例如,xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); // 加上UTF-8是最好 xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
AJAX下的GET與POST
function startRequest(){ // 建立xmlHttp物件 createXMLHttpRequest(); // 向Demo.aspx,使用GET開啟請求 xmlHttp.open("GET","Demo.aspx"); // 狀態有改變時,就執行 xmlHttp.onreadystatechange = function(){ // 當狀態值為4(完成)與狀態碼為200(請求成功) if(xmlHttp.readyState == 4 && xmlHttp.status == 200) // Do something alert("服務器傳回訊息: " + xmlHttp.responseText); } // 傳送HTTP請求到伺服器 // GET記得加上null xmlHttp.send(null); }
IE會自動暫存非同步的結果,解決方法是每次非同步請求的URL不同,我們可以加上一個伺服器不需要的參數,讓每次的URL都不同。
// 加上一個時間戳計當亂數使用 var url = "Demo.aspx?timestamp=" & new Date().getTime(); xmlHttp.open("GET",url, true);
GET請求如果有參數,直接放入URL網址中,例如,
var queryString = "name=Bruce&age=18"; var url = "Demo.aspx?" + queryString + "×tamp=" + new Date().getTime();
POST請求有參數,統一放在send()中來傳送,例如,
var queryString = "name=Bruce&age=18"; var url = "Demo.aspx?timestamp=" & new Date().getTime(); // url放在open() xmlHttp.open("POST",url,true); ... xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8"); // queryString放在send() xmlHttp.send(queryString);
POST方法中文會出現亂碼
在IE8與Firefox上測試已無此問題,但為了相容性還是建議加上解決方法。這是因為XMLHttpRequest物件在處理傳回responseText/XML時是使用UTF-8編碼,建議使用解決方法2,對回傳的資料進行decodeURIComponent()解碼即可。
解決方法一
對要傳送的資料進行兩次encodeURI()編碼,以解決中文亂碼問題,例如,... xmlHttp.send(encodeURI(encodeURI(queryString)));
對傳回資料responseText/XML再進行一次decodeURI()解碼,例如,
responseDiv.innerHTML = decodeURI(xmlHttp.responseText);
解決方法二
setRequestHeader直接宣告(charset)使用UTF-8,例如,xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8"); xmlHttp.send(queryString);
對傳回資料responseText/XML進行decodeURIComponent()解碼,例如,
responseDiv.innerHTML = decodeURIComponent((xmlHttp.responseXML));
解決方法三
ASP,ASP.NET: 輸出之前Response.Charset = "big5 | gb2312",依取得的頁面charset設定。onreadystatechange方法
AJAX的重點在狀態改變後的事件處理函式(handler),也可以說,這是唯一我們必須自訂(手動)設計程式碼的地方,換個方向說,我們在請求/回應,而伺服器回應資料後,我們要如何處理及顯示到網頁上(DOM)。同時觸發非同步問題
如果我們必須同時觸發(呼叫)多個AJAX物件,因為我們通常都是把建立的AJAX物件的xmlHttp宣告為全域變數,所以可能會產生覆蓋的情況。解決同時觸發非同步問題
就是將xmlHttp宣告為區域變數來處理,並在收到伺服器資料且處理完後,手動刪除。function getData(){ var xmlHttp=""; createXMLHttpRequest(); xmlHttp.onreadystatechange = function(){ ... // 處理完立即刪除與釋放 delete xmlHttp; xmlHttp = null; } }
可否請教一下,在 IE9/Chrome 下 responseText 有傳回字串,但在 iPhone / Android 下 readyState=4 及 status = 200 都正確, 就是字串是零字串(Empty), 是哪裡錯了呢? 謝謝!
回覆刪除因看不到你的程式,建議可以做以下處理:
回覆刪除1. 使用 JSON 格式來傳遞 Client <--> Server 的物件資料。
2. 使用 iPhone / Android 模擬器進行除錯。