前言
在現代Web開發中,客戶端與服務器之間的異步通信是構建動態應用的核心能力。無論是傳統的AJAX技術(基于XMLHttpRequest
)還是現代的Fetch API
,它們都為實現這一目標提供了關鍵支持。本文將從底層原理、核心功能、代碼實踐到實際應用場景,系統性地對比和分析這兩種API的異同,幫助你掌握如何在不同場景下選擇最佳解決方案。
文章目錄
- 前言
- 一、XMLHttpRequest API:傳統異步請求的基石
- 1.1 核心概念與工作原理
- 1.2 關鍵功能與高級用法
- 1.3 局限性分析
- 二、Fetch API:現代Web請求的標準方案
- 2.1 設計理念與核心優勢
- 2.2 基礎用法與代碼示例
- 2.3 高級功能實踐
- 2.4 常見問題與解決方案
- 三、XMLHttpRequest與Fetch API的深度對比
- 3.1 功能特性對比
- 3.2 性能與適用場景
- 四、實戰案例:構建一個健壯的HTTP客戶端
- 4.1 基于Fetch的封裝庫
- 4.2 實現文件分片上傳
- 總結
一、XMLHttpRequest API:傳統異步請求的基石
1.1 核心概念與工作原理
XMLHttpRequest
(XHR)是瀏覽器提供的原生API,用于在不刷新頁面的情況下與服務器交換數據。其核心流程包括:
- 實例化對象:通過構造函數創建
XMLHttpRequest
實例。 - 配置請求:指定HTTP方法、URL及是否異步。
- 綁定事件:監聽請求狀態變化(如
onload
、onerror
)。 - 發送請求:調用
send()
方法并處理響應數據。
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onload = function() {if (xhr.status === 200) {console.log(JSON.parse(xhr.responseText));} else {console.error('請求失敗:', xhr.status);}
};
xhr.onerror = function() {console.error('網絡錯誤');
};
xhr.send();
1.2 關鍵功能與高級用法
-
同步與異步模式
// 同步請求(阻塞主線程,不推薦) xhr.open('GET', 'https://api.example.com/data', false); xhr.send(); console.log(xhr.responseText);
-
處理二進制數據
xhr.responseType = 'arraybuffer'; xhr.onload = function() {const buffer = xhr.response;// 處理二進制數據(如圖像或文件) };
-
上傳進度監控
xhr.upload.onprogress = function(event) {const percent = (event.loaded / event.total) * 100;console.log(`上傳進度: ${percent}%`); };
-
超時控制
xhr.timeout = 5000; // 5秒超時 xhr.ontimeout = function() {console.error('請求超時'); };
1.3 局限性分析
- 回調地獄:事件監聽機制導致代碼嵌套復雜。
- 錯誤處理不統一:需手動檢查HTTP狀態碼和網絡錯誤。
- 不支持Promise:與現代異步編程模式不兼容。
二、Fetch API:現代Web請求的標準方案
2.1 設計理念與核心優勢
Fetch API
基于Promise設計,提供更簡潔、靈活的請求方式,并天然支持以下特性:
- 鏈式調用:避免回調嵌套。
- Streams API集成:處理流式數據(如大文件下載)。
- CORS與安全策略:默認不發送跨域Cookie,需顯式配置。
2.2 基礎用法與代碼示例
fetch('https://api.example.com/data').then(response => {if (!response.ok) throw new Error('HTTP錯誤');return response.json();}).then(data => console.log(data)).catch(error => console.error('請求失敗:', error));
2.3 高級功能實踐
-
自定義請求頭與模式
fetch('https://api.example.com/data', {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': 'Bearer token'},body: JSON.stringify({ key: 'value' }),mode: 'cors', // 跨域模式credentials: 'include' // 包含Cookie });
-
中斷請求(AbortController)
const controller = new AbortController(); setTimeout(() => controller.abort(), 5000);fetch('https://api.example.com/data', {signal: controller.signal }).catch(error => {if (error.name === 'AbortError') {console.log('請求被手動取消');} });
-
流式數據處理
fetch('https://api.example.com/large-file').then(response => {const reader = response.body.getReader();return new ReadableStream({start(controller) {function push() {reader.read().then(({ done, value }) => {if (done) {controller.close();return;}controller.enqueue(value);push();});}push();}});}).then(stream => new Response(stream)).then(response => response.blob());
2.4 常見問題與解決方案
-
錯誤處理優化:
async function fetchWithRetry(url, retries = 3) {for (let i = 0; i < retries; i++) {try {const response = await fetch(url);if (!response.ok) throw new Error('HTTP錯誤');return await response.json();} catch (error) {if (i === retries - 1) throw error;await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));}} }
-
超時封裝:
function fetchWithTimeout(url, timeout = 5000) {return Promise.race([fetch(url),new Promise((_, reject) =>setTimeout(() => reject(new Error('請求超時')), timeout))]); }
三、XMLHttpRequest與Fetch API的深度對比
3.1 功能特性對比
特性 | XMLHttpRequest | Fetch API |
---|---|---|
Promise支持 | 否 | 是 |
請求取消 | 是(abort()) | 是(AbortController) |
流式數據處理 | 有限支持 | 完全支持 |
CORS處理 | 需手動配置 | 默認安全策略 |
Service Worker | 不支持 | 完全集成 |
3.2 性能與適用場景
-
XMLHttpRequest適用場景:
- 需要監控上傳/下載進度。
- 兼容舊版瀏覽器(如IE10及以下)。
-
Fetch API推薦場景:
- 現代Web應用開發。
- 需要與Service Worker配合實現離線緩存。
- 處理流式數據或大文件。
四、實戰案例:構建一個健壯的HTTP客戶端
4.1 基于Fetch的封裝庫
class HttpClient {constructor(baseURL, headers = {}) {this.baseURL = baseURL;this.headers = headers;}async request(endpoint, options = {}) {const url = `${this.baseURL}${endpoint}`;const response = await fetch(url, {...options,headers: { ...this.headers, ...options.headers }});if (!response.ok) {const error = new Error(`HTTP ${response.status}`);error.response = response;throw error;}const contentType = response.headers.get('content-type');if (contentType?.includes('application/json')) {return response.json();}return response.text();}get(endpoint, params) {const query = new URLSearchParams(params).toString();return this.request(`${endpoint}?${query}`, { method: 'GET' });}post(endpoint, body) {return this.request(endpoint, {method: 'POST',body: JSON.stringify(body),headers: { 'Content-Type': 'application/json' }});}
}// 使用示例
const api = new HttpClient('https://api.example.com', {Authorization: 'Bearer token'
});
api.get('/users', { page: 1 }).then(users => console.log(users));
4.2 實現文件分片上傳
async function uploadFile(file, chunkSize = 1024 * 1024) {const totalChunks = Math.ceil(file.size / chunkSize);for (let i = 0; i < totalChunks; i++) {const start = i * chunkSize;const end = Math.min(start + chunkSize, file.size);const chunk = file.slice(start, end);const formData = new FormData();formData.append('file', chunk);formData.append('chunkIndex', i);formData.append('totalChunks', totalChunks);await fetch('/upload', {method: 'POST',body: formData});}
}
總結
XMLHttpRequest作為Web異步通信的奠基者,至今仍在特定場景下發揮作用,而Fetch API憑借其現代化的設計正在成為主流選擇。開發者需要根據以下因素決策:
- 瀏覽器兼容性:如需支持舊版瀏覽器,XHR仍是必要選項。
- 功能需求:進度監控、請求取消等特性可能影響技術選型。
- 代碼可維護性:Fetch的Promise鏈與async/await語法更易維護。
未來,隨著Web Streams API和Service Worker的普及,Fetch API將在性能優化和離線體驗領域展現更大潛力。建議在新項目中優先采用Fetch,同時保持對XHR原理的理解以應對遺留系統維護需求。