XMLHttpRequest 完全指南 (2026)
了解 XMLHttpRequest 的工作原理,以及何时应该选择它而非 Fetch。
开始使用 Hypereal 构建
通过单个 API 访问 Kling、Flux、Sora、Veo 等。免费积分开始,扩展到数百万。
无需信用卡 • 10万+ 开发者 • 企业级服务
XMLHttpRequest:完整指南 (2026)
XMLHttpRequest (XHR) 是最初用于从浏览器发起 HTTP 请求的 JavaScript API。尽管在新项目中 Fetch API 已在很大程度上取代了它,但在 2026 年,XMLHttpRequest 在某些特定场景下依然具有重要意义——特别是上传进度追踪、旧版浏览器支持以及维护既有代码库。
本指南涵盖了关于 XMLHttpRequest 你需要了解的一切:基础用法、高级特性、错误处理,以及何时在 XHR 和 Fetch 之间做出选择。
什么是 XMLHttpRequest?
XMLHttpRequest 是一个内置的浏览器 API,允许你在不刷新页面的情况下发送 HTTP 请求并接收响应。尽管名字中带有 "XML",但它可以处理任何格式的数据——不仅仅是 XML。JSON、纯文本、HTML、二进制数据和表单数据都可以通过 XHR 传输。
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data");
xhr.onload = function () {
console.log(JSON.parse(xhr.responseText));
};
xhr.send();
基础 GET 请求
以下是一个最简单的 XHR GET 请求示例:
function fetchUsers() {
const xhr = new XMLHttpRequest();
// 配置请求
xhr.open("GET", "https://api.example.com/v1/users");
// 设置响应类型(可选,默认为 text)
xhr.responseType = "json";
// 处理成功的响应
xhr.onload = function () {
if (xhr.status === 200) {
console.log("Users:", xhr.response);
} else {
console.error("Error:", xhr.status, xhr.statusText);
}
};
// 处理网络错误
xhr.onerror = function () {
console.error("Network error occurred");
};
// 发送请求
xhr.send();
}
fetchUsers();
XHR 生命周期
| 事件 | 触发时机 |
|---|---|
loadstart |
请求开始发送 |
progress |
正在接收数据(周期性触发) |
load |
请求成功完成 |
error |
网络错误(非 404 等 HTTP 错误) |
abort |
请求通过 xhr.abort() 被取消 |
timeout |
请求超过了设定的超时时间 |
loadend |
请求结束(在 load、error 或 abort 之后触发) |
使用 JSON 发送 POST 请求
function createUser(userData) {
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://api.example.com/v1/users");
// 设置请求头
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Authorization", "Bearer your_token_here");
xhr.responseType = "json";
xhr.onload = function () {
if (xhr.status === 201) {
console.log("User created:", xhr.response);
} else {
console.error("Failed:", xhr.status, xhr.response);
}
};
xhr.onerror = function () {
console.error("Network error");
};
// 发送 JSON 数据
xhr.send(JSON.stringify(userData));
}
createUser({
name: "Jane Smith",
email: "jane@example.com",
role: "developer",
});
PUT 和 DELETE 请求
// PUT 请求用于更新资源
function updateUser(id, data) {
const xhr = new XMLHttpRequest();
xhr.open("PUT", `https://api.example.com/v1/users/${id}`);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.responseType = "json";
xhr.onload = function () {
if (xhr.status === 200) {
console.log("Updated:", xhr.response);
}
};
xhr.send(JSON.stringify(data));
}
// DELETE 请求用于移除资源
function deleteUser(id) {
const xhr = new XMLHttpRequest();
xhr.open("DELETE", `https://api.example.com/v1/users/${id}`);
xhr.setRequestHeader("Authorization", "Bearer your_token_here");
xhr.onload = function () {
if (xhr.status === 204) {
console.log("Deleted successfully");
}
};
xhr.send();
}
设置请求头
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/v1/data");
// 身份验证
xhr.setRequestHeader("Authorization", "Bearer token123");
// API Key
xhr.setRequestHeader("X-API-Key", "sk_live_abc123");
// 内容类型 (针对 POST/PUT)
xhr.setRequestHeader("Content-Type", "application/json");
// 自定义请求头
xhr.setRequestHeader("X-Request-ID", "req_abc123");
xhr.setRequestHeader("Accept-Language", "en-US");
xhr.send();
读取响应头
xhr.onload = function () {
// 获取特定响应头
const contentType = xhr.getResponseHeader("Content-Type");
const rateLimit = xhr.getResponseHeader("X-RateLimit-Remaining");
// 以字符串形式获取所有响应头
const allHeaders = xhr.getAllResponseHeaders();
console.log(allHeaders);
};
响应类型 (Response Types)
XHR 支持多种响应类型:
| responseType | xhr.response 类型 | 使用场景 |
|---|---|---|
"" (默认值) |
String | 纯文本, HTML |
"text" |
String | 与默认值相同 |
"json" |
Object | API 响应数据 |
"blob" |
Blob | 图片, 文件 |
"arraybuffer" |
ArrayBuffer | 二进制数据 |
"document" |
Document | XML/HTML 解析 |
// JSON 响应
const xhr1 = new XMLHttpRequest();
xhr1.responseType = "json";
xhr1.onload = () => console.log(xhr1.response.name); // 直接访问对象
// Blob 响应(用于下载文件)
const xhr2 = new XMLHttpRequest();
xhr2.responseType = "blob";
xhr2.onload = () => {
const url = URL.createObjectURL(xhr2.response);
const a = document.createElement("a");
a.href = url;
a.download = "file.pdf";
a.click();
URL.revokeObjectURL(url);
};
// ArrayBuffer(用于二进制处理)
const xhr3 = new XMLHttpRequest();
xhr3.responseType = "arraybuffer";
xhr3.onload = () => {
const data = new Uint8Array(xhr3.response);
console.log("Bytes received:", data.length);
};
上传进度追踪
这是 XHR 优于 Fetch API 的地方。XHR 提供了粒度化的上传和下载进度事件:
function uploadFile(file) {
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append("file", file);
// 上传进度
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
const percentComplete = Math.round((event.loaded / event.total) * 100);
console.log(`Upload progress: ${percentComplete}%`);
updateProgressBar(percentComplete);
}
};
xhr.upload.onload = function () {
console.log("Upload complete!");
};
// 下载进度(针对响应内容)
xhr.onprogress = function (event) {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
console.log(`Download progress: ${percent}%`);
}
};
xhr.onload = function () {
if (xhr.status === 200) {
console.log("Server response:", JSON.parse(xhr.responseText));
}
};
xhr.open("POST", "https://api.example.com/v1/upload");
xhr.send(formData);
}
// 配合文件输入框使用
document.getElementById("fileInput").addEventListener("change", (e) => {
uploadFile(e.target.files[0]);
});
进度条 HTML
<input type="file" id="fileInput" />
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<span id="progressText">0%</span>
<script>
function updateProgressBar(percent) {
document.getElementById("progressFill").style.width = percent + "%";
document.getElementById("progressText").textContent = percent + "%";
}
</script>
超时与中止
设置超时
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/v1/slow-endpoint");
// 设置 10 秒超时
xhr.timeout = 10000;
xhr.ontimeout = function () {
console.error("Request timed out after 10 seconds");
};
xhr.onload = function () {
console.log("Response received:", xhr.response);
};
xhr.send();
中止请求
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/v1/large-data");
xhr.onabort = function () {
console.log("Request was cancelled");
};
xhr.send();
// 3 秒后取消请求
setTimeout(() => {
xhr.abort();
}, 3000);
错误处理
一个成熟的错误处理模式如下:
function apiRequest(method, url, data = null) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.responseType = "json";
xhr.timeout = 15000;
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject({
status: xhr.status,
statusText: xhr.statusText,
response: xhr.response,
});
}
};
xhr.onerror = function () {
reject({ status: 0, statusText: "Network Error", response: null });
};
xhr.ontimeout = function () {
reject({ status: 0, statusText: "Timeout", response: null });
};
xhr.onabort = function () {
reject({ status: 0, statusText: "Aborted", response: null });
};
xhr.send(data ? JSON.stringify(data) : null);
});
}
// 使用 async/await 配合
async function getUsers() {
try {
const users = await apiRequest("GET", "https://api.example.com/v1/users");
console.log(users);
} catch (error) {
if (error.status === 401) {
console.log("Please log in again");
} else if (error.status === 0) {
console.log("Network issue:", error.statusText);
} else {
console.log("API error:", error.status, error.response);
}
}
}
ReadyState 状态值
XHR 通过 readyState 追踪请求生命周期:
| 状态值 | 常量 | 含义 |
|---|---|---|
| 0 | UNSENT |
open() 尚未调用 |
| 1 | OPENED |
open() 已调用 |
| 2 | HEADERS_RECEIVED |
已接收到响应头 |
| 3 | LOADING |
正在加载响应体 |
| 4 | DONE |
请求已完成 |
xhr.onreadystatechange = function () {
switch (xhr.readyState) {
case XMLHttpRequest.OPENED:
console.log("Request opened");
break;
case XMLHttpRequest.HEADERS_RECEIVED:
console.log("Headers:", xhr.getAllResponseHeaders());
break;
case XMLHttpRequest.LOADING:
console.log("Loading data...");
break;
case XMLHttpRequest.DONE:
console.log("Request complete, status:", xhr.status);
break;
}
};
XMLHttpRequest 对比 Fetch API
| 特性 | XMLHttpRequest | Fetch API |
|---|---|---|
| 语法 | 基于回调/事件 | 基于 Promise |
| 上传进度 | 支持 (xhr.upload.onprogress) |
无原生支持 |
| 下载进度 | 支持 (xhr.onprogress) |
支持 (通过 ReadableStream) |
| 中止 | xhr.abort() |
AbortController |
| 超时 | 内置支持 (xhr.timeout) |
需要配合 AbortController 手动实现 |
| 流式传输 (Streaming) | 不支持 | 支持 |
| Service Workers | 不支持 | 支持 |
| CORS | 行为一致 | 行为一致 |
| 请求/响应对象 | 无 | 有 (可重用) |
何时使用 XMLHttpRequest
- 你需要追踪上传进度(带有进度条的文件上传)
- 你在处理已经使用了 XHR 的旧项目代码
- 你需要内置的超时支持而不想编写额外的控制逻辑
何时使用 Fetch
- 新项目 —— Fetch 拥有更简洁的 API
- 流式响应 —— Fetch 支持 ReadableStream
- Service Workers —— Fetch 是唯一的选择
- 服务端 JavaScript —— Fetch 已在 Node.js 18+ 中可用
发送表单数据
// 使用 FormData (用于文件上传和表单提交)
const formData = new FormData();
formData.append("name", "Jane");
formData.append("email", "jane@example.com");
formData.append("avatar", fileInput.files[0]);
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://api.example.com/v1/users");
// 不要设置 Content-Type —— 浏览器会自动设置带 boundary 的请求头
xhr.send(formData);
// 使用 URL 编码数据
const xhr2 = new XMLHttpRequest();
xhr2.open("POST", "https://api.example.com/v1/login");
xhr2.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr2.send("username=jane&password=secret123");
总结
在 2026 年,XMLHttpRequest 仍然是一个功能强大且相关的 API,尤其是在上传进度追踪和维护既有代码库方面。虽然 Fetch 是新项目的默认选择,但理解 XHR 对于维护现有代码以及在需要其独特功能(如上传进度)的场景下依然极具价值。
如果你正在构建需要上传媒体文件或显示生成进度的 Web 应用程序,Hypereal AI 提供了支持进度追踪的 AI 图像和视频生成 API——这正是 XMLHttpRequest 进度事件的完美应用场景。
