跳轉到內容

JavaScript/XMLHttpRequest

來自華夏公益教科書


這是一個關於 XMLHttpRequest 物件的指南和參考,它是 Ajax 程式設計 中的關鍵元件。

這是一個使用 XMLHttpRequest 的網頁簡短示例。稍後我們會詳細介紹各種問題以及解決方法。

<html><!-- example.html : public domain -->
<script language="JavaScript" type="text/JavaScript" >
function alertContents(httpRequest) {
  if (httpRequest.readyState == 4) {
    // Everything is good, the response is received
    if ((httpRequest.status == 200) || (httpRequest.status == 0)) {
      // FIXME: perhaps a better example is to *replace* some text in the page.
      var htmlDoc = document.createElement('div'); // Create a new, empty DIV node.
      htmlDoc.innerHTML = httpRequest.responseText; // Place the returned HTML page inside the new node.
      alert("The response was: " + httpRequest.status + httpRequest.responseText);
    } else {
      alert('There was a problem with the request. ' + httpRequest.status + httpRequest.responseText);
    }
  }
}

function send_with_ajax(the_url) {
  var httpRequest = new XMLHttpRequest();
  httpRequest.onreadystatechange = function() { alertContents(httpRequest); };
  httpRequest.open("GET", the_url, true);
  httpRequest.send(null);
}
</script>

<p onClick="send_with_ajax('example.html');">
Click me!
</p>
</html>

物件參考

[編輯 | 編輯原始碼]
abort()
取消當前請求。
getAllResponseHeaders()
以字串形式返回完整的 HTTP 標頭集。
getResponseHeader(headerName)
返回指定 HTTP 標頭的值。
open(method, URL)
open(method, URL, async)
open(method, URL, async, userName)
open(method, URL, async, userName, password)
指定請求的方法、URL 和其他可選屬性。
  • method引數可以具有以下值GET, POST, HEAD, PUT, DELETE,或者在 W3C 規範中列出的其他 HTTP 方法。
  • URL引數可以是相對 URL 或完整 URL。
  • async引數指定請求是非同步處理還是同步處理 -true表示指令碼處理在send()方法之後繼續執行,而不等待響應,而false表示指令碼在繼續指令碼處理之前等待響應。
send(content)
傳送請求。content可以是字串或文件引用。
setRequestHeader(label, value)
新增一個label/value對要傳送的 HTTP 標頭。
onreadystatechange
指定對在每個狀態更改時觸發的事件的事件處理程式的引用
readyState
按如下方式返回物件的狀態
  • 0 = 未初始化 -open()尚未被呼叫。
  • 1 = 開啟 -send()尚未被呼叫。
  • 2 = 已傳送 -send()已被呼叫,標頭和狀態可用。
  • 3 = 正在接收 - 正在下載,responseText包含部分資料
  • 4 = 已載入 - 完成。
responseText
以字串形式返回響應。
responseXML
以 XML 形式返回響應。此屬性返回一個 XML 文件物件,可以使用 W3C DOM 節點樹方法和屬性對其進行檢查和解析。
responseBody
以二進位制編碼字串形式返回響應。此屬性不是原生 XMLHttpRequest 包裝器的一部分。要使用此屬性,必須使用 ActiveX 元件建立 XHR 物件。JScript 示例
if (typeof ActiveXObject !== "undefined") {
  xmlhttp = new ActiveXObject("MSXML2.XMLHTTP");
  xmlhttp.open("GET", "#", false);
  xmlhttp.send(null);
  alert(xmlhttp.responseBody);
} else {
  alert("This browser does not support Microsoft ActiveXObjects.")
}
status
以數字形式返回 HTTP 狀態程式碼(例如,404 代表“未找到”,200 代表“確定”)。某些與網路相關的狀態程式碼(例如,408 代表“請求超時”)會導致 Firefox 在訪問狀態欄位時丟擲錯誤。如果伺服器沒有(正確地)響應,IE 會返回一個 WinInet 錯誤程式碼(例如,12029 代表“無法連線”)。
statusText
以字串形式返回狀態(例如,“未找到”或“確定”)。

錯誤和不一致

[編輯 | 編輯原始碼]

處理 XMLHttpRequest 實現中的錯誤和不一致

大多數實現也實現了 HTTP 快取。Internet Explorer 和 Firefox 都是如此,但它們重新驗證快取資料的機制和時機不同。Firefox 在每次重新整理頁面時都會重新驗證快取的響應,並使用“Last-Modified”標頭的值作為“If-Modified-Since”標頭的值,該標頭包含在快取的響應中。

Internet Explorer 只有在快取的響應過期後(即,超過“Expires”標頭接收到的日期)才會這樣做。這引發了一些問題,因為 Internet Explorer 中存在一個錯誤,導致快取的響應永遠不會重新整理。

可以在客戶端統一快取行為。以下指令碼演示了一種示例方法

var request = new XMLHttpRequest();
request.open("GET", url, false);
request.send(null);
if (!request.getResponseHeader("Date")) {
  var cached = request;
  request = new XMLHttpRequest();
  var ifModifiedSince =
  cached.getResponseHeader("Last-Modified") ||
  new Date(0); // January 1, 1970
  request.open("GET", url, false);
  request.setRequestHeader("If-Modified-Since", ifModifiedSince);
  request.send("");
  if (request.status == 304) {
    request = cached;
  }
}

在 Internet Explorer 中,如果從快取中返回響應而沒有重新驗證,則“Date”標頭將為空字串。解決方法是檢查“Date”響應標頭,並在需要時發出另一個請求。如果需要第二個請求,實際的 HTTP 請求不會被髮出兩次,因為第一個呼叫不會產生實際的 HTTP 請求。

對快取請求的引用被保留,因為如果第二個呼叫的響應程式碼/狀態是“304 Not Modified”,則響應主體將變為空字串(""),然後需要返回快取的物件。節省記憶體和避免建立第二個物件的方法是隻保留必要的響應資料並重用 XMLHttpRequest 物件。

以上指令碼基於伺服器始終發出“Date”標頭的假設,對於大多數伺服器配置來說應該是正確的。此外,它還演示了伺服器和客戶端之間的同步通訊。在非同步通訊的情況下,應在回撥期間進行檢查。

這個問題通常可以透過採用完全阻止快取的技術來解決。不加選擇地使用這些技術會導致效能低下並浪費網路頻寬。

如果指令碼執行具有副作用的操作(例如,新增評論、將訊息標記為已讀),這些操作需要請求始終到達最終伺服器,則應使用POST方法。

解決方法

[編輯 | 編輯原始碼]

Internet Explorer 還會快取動態頁面,這是一個問題,因為頁面的 URL 可能不會改變,但內容會改變(例如,新聞提要)。可以透過新增唯一的時間戳或隨機數(可能兩者都加),通常使用 Date 物件和/或 Math.random() 來實現這種情況的解決方法。

對於簡單的文件請求,可以使用查詢字串分隔符“?”,或者對於現有的查詢,可以在最後一個“&”之後新增一個最後的子查詢,將唯一的查詢項附加到現有查詢中。缺點是每個這樣的請求都會用無用的(永遠不會重複使用)內容填充快取,而這些內容原本可以用於其他快取內容(更有效的資料將從快取中清除以騰出空間來存放這些一次性響應)。

更好的解決方法是將元標記新增到動態頁面,以使其不可快取

<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />

在 IE 中重用 XMLHttpRequest 物件

[編輯 | 編輯原始碼]

在 IE 中,如果在設定 onreadystatechange 回撥函式後呼叫 open 方法,在嘗試重用 XHR 物件時會出現問題。為了能夠正確地重用 XHR 物件,請先使用 open 方法,然後設定 onreadystatechange。 這是因為 IE 在 open 方法中隱式地重置了物件,如果狀態為 'completed'。 有關重用的更多解釋:在 IE 中重用 XMLHttpRequest 物件。 在設定回撥函式後呼叫 open 方法的缺點是會失去對 readystates 的跨瀏覽器支援。 請參見quirksmode 文章。

進一步閱讀

[編輯 | 編輯原始碼]


華夏公益教科書