http request-01-XMLHttpRequest XHR 簡單介紹

老马啸西风發表於2024-08-17

http 請求系列

http request-01-XMLHttpRequest XHR 簡單介紹

http request-01-XMLHttpRequest XHR 標準

Ajax 詳解-01-AJAX(Asynchronous JavaScript and XML)入門介紹

Ajax XHR 的替代方案-fetch

Ajax XHR 的替代方案-fetch 標準

Ajax 的替代方案-axios.js

http 請求-04-promise 物件 + async/await

XHR 的官方學習資料 給出網址

以下是一些官方學習 XMLHttpRequest (XHR) 的資源網址:

  1. MDN Web Docs - XMLHttpRequest

    • MDN XMLHttpRequest Documentation
    • 這是最權威和詳細的 XHR 文件,提供了關於如何使用 XMLHttpRequest 的全面介紹,包括屬性、方法、事件和示例程式碼。
  2. WhatWG - XMLHttpRequest Standard

    • WhatWG XMLHttpRequest Standard
    • WhatWG 的標準文件詳細描述了 XMLHttpRequest 的規範和實現細節,是瞭解其正式標準的良好資源。
  3. W3C - XMLHttpRequest Level 2 Specification

    • W3C XMLHttpRequest Level 2
    • W3C 的規範文件提供了 XMLHttpRequest Level 2 的詳細描述,包括新增的功能和改進。

這些資源將幫助你深入理解 XMLHttpRequest 的功能、用法和規範。

XHR 是什麼?

XMLHttpRequest(XHR)是一個用於在客戶端和伺服器之間進行非同步 HTTP 請求的 API,廣泛用於動態更新網頁內容,而無需重新載入整個頁面。

儘管它最初是為了處理 XML 資料而設計的,但它現在支援多種資料格式,並已成為 Web 開發中重要的工具。

1. 基本概念

XMLHttpRequest 提供了在客戶端(如瀏覽器)與伺服器之間進行非同步通訊的能力,使得網頁可以在不重新載入頁面的情況下更新內容。

它的主要用途包括動態載入資料、提交表單以及與伺服器進行實時互動。

2. 核心方法和屬性

2.1 建立請求

const xhr = new XMLHttpRequest();

2.2 配置請求

  • open(method, url, async, user, password): 初始化請求。
    • method: HTTP 方法(如 GETPOST)。
    • url: 請求的 URL。
    • async: 是否非同步(truefalse)。
    • userpassword: 可選的身份驗證資訊。
xhr.open('GET', 'https://example.com/data', true);

2.3 傳送請求

  • send(body): 傳送請求。body 引數用於傳送資料(對於 GET 請求,通常為空)。
xhr.send();

2.4 事件處理

  • onload: 請求成功完成時觸發。
  • onerror: 請求失敗時觸發。
  • onprogress: 請求進度更新時觸發(適用於進度監控)。
  • onabort: 請求被取消時觸發。
  • onreadystatechange: 請求狀態改變時觸發。
xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log(xhr.responseText);
  } else {
    console.error('Request failed with status:', xhr.status);
  }
};

xhr.onerror = function() {
  console.error('Request error');
};

2.5 重要屬性

  • readyState: 請求的當前狀態(0: UNSENT, 1: OPENED, 2: HEADERS_RECEIVED, 3: LOADING, 4: DONE)。
  • status: HTTP 響應狀態碼(如 200、404、500)。
  • statusText: 響應狀態文字。
  • responseText: 響應的文字內容。
  • responseXML: 響應的 XML 文件(如果 Content-Typeapplication/xmltext/xml)。
  • response: 根據響應型別(如 ArrayBufferBlob)返回的響應資料。

3. 使用示例

3.1 簡單 GET 請求

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
xhr.onload = function() {
  if (xhr.status === 200) {
    console.log('Response:', xhr.responseText);
  } else {
    console.error('Error:', xhr.status, xhr.statusText);
  }
};
xhr.send();

3.2 POST 請求

const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://jsonplaceholder.typicode.com/posts', true);
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.onload = function() {
  if (xhr.status === 201) {
    console.log('Created:', xhr.responseText);
  } else {
    console.error('Error:', xhr.status, xhr.statusText);
  }
};
const data = JSON.stringify({ title: 'foo', body: 'bar', userId: 1 });
xhr.send(data);

3.3 進度監控

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
xhr.onprogress = function(event) {
  if (event.lengthComputable) {
    const percentComplete = (event.loaded / event.total) * 100;
    console.log('Progress:', percentComplete.toFixed(2) + '%');
  }
};
xhr.onload = function() {
  if (xhr.status === 200) {
    console.log('Response:', xhr.responseText);
  } else {
    console.error('Error:', xhr.status, xhr.statusText);
  }
};
xhr.send();

4. 優缺點

4.1 優點

  • 廣泛支援: 相容大多數瀏覽器,包括較早版本。
  • 靈活性: 支援多種 HTTP 方法和請求體格式。
  • 進度監控: 能夠監控請求的進度和狀態變化。

4.2 缺點

  • 複雜性: 處理非同步操作和錯誤處理可能比較複雜,特別是巢狀的回撥函式(“回撥地獄”)。
  • 現代替代品: 比如 fetch API 提供了更簡潔的語法和更強大的功能,例如流處理和更好的錯誤處理。

5. 現代替代

XMLHttpRequest 的一些功能和用途已經被更現代的 fetch API 所取代。fetch 提供了更簡潔的語法、支援 Promise 和流處理,使得非同步操作和錯誤處理更為直觀。

XHR 為什麼需要?解決了什麼問題

XMLHttpRequest (XHR) 是一種用於在瀏覽器和伺服器之間進行非同步通訊的 API。它在 Web 開發中解決了多個關鍵問題,特別是在使用者體驗和動態網頁更新方面。

以下是 XMLHttpRequest 需要的原因及其解決的問題:

1. 頁面非同步更新

問題

  • 傳統的網頁請求: 每次需要從伺服器獲取新資料或提交資料時,整個頁面必須重新載入。這種方式會導致使用者體驗不流暢,頁面重新整理較慢。

解決方案

  • XHR 的非同步請求: 允許網頁在不重新載入整個頁面的情況下,從伺服器獲取資料並更新部分頁面內容。這使得網頁可以實現區域性更新,而不是每次都重新整理整個頁面,從而提高了使用者體驗和響應速度。

2. 提高使用者體驗

問題

  • 頁面停頓: 在傳統的同步請求中,使用者必須等待整個請求和響應過程完成,這可能導致頁面停頓或凍結。

解決方案

  • 非同步處理: XMLHttpRequest 透過非同步處理請求,允許瀏覽器在發出請求時繼續執行其他操作,不會阻塞使用者介面的互動。這樣,使用者可以繼續與網頁進行互動,而不會受到請求等待的影響。

3. 動態內容載入

問題

  • 靜態網頁內容: 在沒有非同步請求的情況下,網頁內容通常是靜態的,所有內容都在頁面載入時獲取。

解決方案

  • 動態內容載入: 透過 XMLHttpRequest,網頁可以在執行時動態載入資料,並根據需要更新內容。比如,使用者滾動到頁面底部時自動載入更多資料(如無限滾動),或者基於使用者輸入實時更新搜尋結果。

4. 後臺資料處理

問題

  • 使用者互動時的資料提交: 使用者在填寫表單時,資料提交通常會導致頁面重新載入,影響使用者體驗。

解決方案

  • 表單提交: 使用 XMLHttpRequest 可以在不重新整理頁面的情況下提交表單資料。這使得使用者可以在不離開當前頁面的情況下完成資料提交和處理。

5. 區域性更新和請求管理

問題

  • 整體更新難度: 如果需要對部分頁面內容進行更新,傳統方法通常需要複雜的頁面過載和資料處理。

解決方案

  • 區域性更新: XMLHttpRequest 允許透過指定的 URL 和請求方法,只更新頁面的某一部分內容。這簡化了區域性更新的實現,並減少了對伺服器的負擔。

6. 進度監控

問題

  • 缺乏反饋: 在傳統請求中,使用者無法獲得請求進度的資訊(如正在下載的進度)。

解決方案

  • 進度事件: XMLHttpRequest 提供了 onprogress 事件,使得開發者可以實時監控請求的進度,並向使用者提供反饋,例如顯示下載進度條。

7. 與其他技術的相容性

問題

  • 舊版瀏覽器支援: 在 fetch API 出現之前,許多舊版瀏覽器不支援現代的 HTTP 請求 API。

解決方案

  • 相容性: XMLHttpRequest 作為較早的標準,得到了廣泛的瀏覽器支援,包括舊版瀏覽器。這使得它在需要相容舊版瀏覽器的應用中仍然非常重要。

XHR 的適用場景,優缺點

XMLHttpRequest(XHR)在 Web 開發中有一些特定的適用場景,以及它的優點和缺點。

以下是一些實際應用場景和它們的優缺點,用更接地氣的方式來解釋:

適用場景

  1. 動態內容更新

    • 場景: 當你希望在使用者與網頁互動時,動態更新頁面的一部分內容,例如載入評論、文章或產品列表,而不需要重新載入整個頁面。
    • 例子: 社交媒體平臺上的帖子載入,電商網站上的產品篩選。
  2. 非同步表單提交

    • 場景: 當使用者填寫表單並提交資料時,你希望在後臺處理這些資料,而不重新整理整個頁面。
    • 例子: 使用者在評論框中輸入內容並提交,資料直接上傳到伺服器,頁面不會過載。
  3. 區域性資料重新整理

    • 場景: 需要重新整理網頁的某個部分的資料,而不是整個頁面。例如,實時更新價格或天氣資訊。
    • 例子: 監控儀表板上更新伺服器狀態,新聞網站上實時更新新聞頭條。
  4. 進度反饋

    • 場景: 需要監控和展示檔案上傳或下載的進度,讓使用者知道操作的進展。
    • 例子: 檔案上傳表單,下載大檔案時顯示進度條。
  5. 舊版瀏覽器相容

    • 場景: 如果你的應用需要支援舊版瀏覽器,它可能不支援現代的 fetch API,但支援 XMLHttpRequest
    • 例子: 針對使用老舊裝置或瀏覽器的使用者群體的應用。

優點

  1. 廣泛支援

    • 優點: 幾乎所有的瀏覽器都支援 XMLHttpRequest,包括許多舊版瀏覽器。
    • 實際感受: 你不需要擔心使用者的瀏覽器是否支援,因為它幾乎能在所有環境中工作。
  2. 靈活性

    • 優點: 支援多種 HTTP 方法(GET、POST、PUT、DELETE 等),並且能夠處理不同格式的響應資料。
    • 實際感受: 可以用來做各種各樣的請求,比如從伺服器獲取 JSON 資料或傳送表單資料。
  3. 進度監控

    • 優點: 提供 onprogress 事件,可以監控請求的進度。
    • 實際感受: 你可以給使用者提供下載或上傳的實時進度,提升使用者體驗。

缺點

  1. 複雜性

    • 缺點: 處理非同步請求和響應可能會很複雜,尤其是在巢狀回撥函式(“回撥地獄”)的情況下。
    • 實際感受: 編寫程式碼時可能會變得混亂,不容易維護。
  2. 沒有內建的 Promise 支援

    • 缺點: 不像 fetch API,XMLHttpRequest 預設不支援 Promise,處理非同步操作時需要使用回撥函式。
    • 實際感受: 需要額外的工作來處理非同步操作,程式碼可能不夠簡潔。
  3. 較少的功能擴充套件

    • 缺點: 與現代 API 比如 fetch 相比,XMLHttpRequest 在流處理和功能擴充套件方面有所限制。
    • 實際感受: 如果需要更高階的功能,比如流式處理大檔案,XMLHttpRequest 可能無法滿足需求。

XHR 的使用最佳實踐

使用 XMLHttpRequest (XHR) 時,遵循一些最佳實踐可以幫助你寫出更高效、可靠和可維護的程式碼。以下是一些實際的最佳實踐:

1. 使用非同步請求

  • 實踐: 總是將 XMLHttpRequestasync 引數設定為 true,以便非同步處理請求。
  • 理由: 非同步請求不會阻塞使用者介面,提高了使用者體驗。
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/data', true);
xhr.send();

2. 處理請求狀態變化

  • 實踐: 使用 onreadystatechange 事件來處理請求的不同狀態。確保檢查 readyStatestatus 屬性來確定請求是否成功完成。
  • 理由: readyState 提供了請求的當前狀態,status 表示 HTTP 響應狀態碼,幫助你正確處理響應或錯誤。
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/data', true);
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) { // DONE
    if (xhr.status === 200) {
      console.log('Response:', xhr.responseText);
    } else {
      console.error('Error:', xhr.status, xhr.statusText);
    }
  }
};
xhr.send();

3. 設定適當的請求頭

  • 實踐: 根據需要設定請求頭(例如 Content-Type)。如果你傳送 JSON 資料,確保設定 Content-Typeapplication/json
  • 理由: 請求頭可以影響伺服器如何處理請求體,確保請求和響應資料格式的正確性。
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://example.com/data', true);
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
const data = JSON.stringify({ key: 'value' });
xhr.send(data);

4. 處理錯誤和超時

  • 實踐: 使用 onerror 事件處理網路錯誤,使用 ontimeout 處理超時情況。設定超時時間以防止請求掛起過久。
  • 理由: 錯誤處理可以提供更好的使用者反饋和恢復機制,而超時處理可以避免長時間等待無響應的請求。
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/data', true);
xhr.timeout = 5000; // 設定超時時間為 5000 毫秒

xhr.ontimeout = function() {
  console.error('Request timed out');
};

xhr.onerror = function() {
  console.error('Request error');
};

xhr.onload = function() {
  if (xhr.status === 200) {
    console.log('Response:', xhr.responseText);
  }
};

xhr.send();

5. 監控進度

  • 實踐: 使用 onprogress 事件來監控請求的進度,尤其是處理大檔案上傳或下載時。
  • 理由: 進度反饋可以改善使用者體驗,讓使用者瞭解操作的進展。
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/largefile', true);
xhr.onprogress = function(event) {
  if (event.lengthComputable) {
    const percentComplete = (event.loaded / event.total) * 100;
    console.log('Progress:', percentComplete.toFixed(2) + '%');
  }
};
xhr.onload = function() {
  if (xhr.status === 200) {
    console.log('File downloaded successfully');
  }
};
xhr.send();

6. 避免回撥地獄

  • 實踐: 使用清晰的回撥結構或考慮使用 Promises(如果需要相容性,可以用 Promise 封裝 XHR)。雖然 XHR 本身不支援 Promise,但你可以建立自己的封裝。
  • 理由: 清晰的回撥結構或使用 Promise 可以提高程式碼的可讀性和可維護性。
function fetchData(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onload = function() {
      if (xhr.status === 200) {
        resolve(xhr.responseText);
      } else {
        reject(new Error(`Request failed with status ${xhr.status}`));
      }
    };
    xhr.onerror = function() {
      reject(new Error('Network error'));
    };
    xhr.send();
  });
}

// 使用
fetchData('https://example.com/data')
  .then(response => console.log('Response:', response))
  .catch(error => console.error('Error:', error));

7. 處理不同資料格式

  • 實踐: 根據伺服器響應的 Content-Type 處理資料。例如,如果響應是 JSON 格式,則呼叫 responseText 並使用 JSON.parse 解析。
  • 理由: 正確處理不同的資料格式確保資料解析的準確性。
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/data', true);
xhr.onload = function() {
  if (xhr.status === 200) {
    const contentType = xhr.getResponseHeader('Content-Type');
    if (contentType.includes('application/json')) {
      const data = JSON.parse(xhr.responseText);
      console.log('JSON data:', data);
    } else {
      console.log('Response:', xhr.responseText);
    }
  }
};
xhr.send();

8. 保持程式碼簡潔

  • 實踐: 儘量保持 XHR 請求程式碼簡潔,避免過多的巢狀和複雜邏輯。封裝常用邏輯為函式可以提高可重用性。
  • 理由: 簡潔的程式碼更易於維護和理解。
function createRequest(method, url, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open(method, url, true);
  xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
      callback(null, xhr.responseText);
    } else {
      callback(new Error(`Request failed with status ${xhr.status}`));
    }
  };
  xhr.onerror = function() {
    callback(new Error('Network error'));
  };
  xhr.send();
}

// 使用
createRequest('GET', 'https://example.com/data', (error, response) => {
  if (error) {
    console.error('Error:', error);
  } else {
    console.log('Response:', response);
  }
});

總結

遵循這些最佳實踐可以幫助你在使用 XMLHttpRequest 時編寫更高效、可靠和易於維護的程式碼。

雖然 fetch API 提供了更現代的介面,但瞭解和掌握 XMLHttpRequest 仍然在許多情況下很重要,尤其是在處理相容性問題時。