XMLHttpRequest 对象
XMLHttpRequest(XHR)对象 —— 现代前端的「老将」与「活化石」
虽然现在大家都用 fetch(),但 XMLHttpRequest 依然是前端面试、底层库(Axios、jQuery)、老项目、企业系统中最常见、最重要的 AJAX 技术。很多高级特性(上传进度、超时控制、Abort、同步请求等)fetch 至今仍需 polyfill,而 XHR 原生就支持。
1. 基本信息一览(2025 年最新状态)
项目值 / 说明构造函数new XMLHttpRequest()全局可用性所有浏览器(包括 IE11+) + Node.js(需 polyfill)是否过时不推荐新建(MDN 已标记为 Legacy),但仍被广泛使用替代品fetch()(推荐)唯一优势(至今不可替代)上传/下载进度、AbortController 兼容性、同步请求、FormData 上传进度等
2. 完整生命周期与事件(必须背下来的 6 个事件)
const xhr = new XMLHttpRequest();
// 1. 关键事件(按顺序触发)
xhr.onloadstart = () => console.log("开始请求");
xhr.onprogress = (e) => {
if (e.lengthComputable) {
console.log(`已接收 ${e.loaded} / ${e.total} 字节`);
}
};
xhr.onload = () => console.log("请求完成(成功或失败)");
xhr.onloadend = () => console.log("请求彻底结束");
xhr.onerror = () => console.log("网络错误");
xhr.ontimeout = () => console.log("超时");
xhr.onabort = () => console.log("手动中止");
// 2. 就绪状态变化(经典)
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) { // DONE
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
console.log("成功", xhr.responseText);
}
}
console.log("readyState:", xhr.readyState);
// 0 UNSENT → 1 OPENED → 2 HEADERS_RECEIVED → 3 LOADING → 4 DONE
};
3. 核心属性与方法(高频面试点)
类型名称说明方法open(method, url, async?, user?, password?)初始化请求,async 默认 truesend(body?)发送请求,body 可以是 string、Document、Blob、FormData 等setRequestHeader(name, value)设置请求头(必须在 open() 之后、send() 之前)abort()中止请求,触发 onabortgetResponseHeader(name)获取单个响应头getAllResponseHeaders()获取所有响应头(字符串,\r\n 分隔)属性readyState0–4 五个状态status / statusTextHTTP 状态码和文字response响应体(根据 responseType 自动解析)responseText永远是字符串(即使出错)responseXML如果是 XML 且响应头正确,会自动解析为 DocumentresponseURL最终重定向后的 URL(非常有用)timeout超时毫秒数(0 表示永不超时)withCredentials是否发送跨域 cookie(CORS 需要)upload上传专用的 XHRUpload 对象,可监听上传进度!responseType”
4. 经典完整示例(包含所有实战技巧)
function ajax(options) {
const xhr = new XMLHttpRequest();
// 1. 基础配置
xhr.open(options.method || 'GET', options.url, true);
// 2. 超时与中止
xhr.timeout = options.timeout || 10000;
xhr.ontimeout = () => options.error?.('请求超时');
const controller = new AbortController(); // 可外部 abort
options.signal?.addEventListener('abort', () => xhr.abort());
// 3. 上传进度(FormData 上传文件必备)
if (xhr.upload && options.onProgress) {
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
options.onProgress(e.loaded / e.total * 100);
}
};
}
// 4. 响应类型
xhr.responseType = options.responseType || 'json';
// 5. 请求头
if (options.headers) {
for (const [k, v] of Object.entries(options.headers)) {
xhr.setRequestHeader(k, v);
}
}
// 6. 跨域带 cookie
if (options.withCredentials) xhr.withCredentials = true;
// 7. 成功回调
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
options.success?.(xhr.response, xhr);
} else {
options.error?.(new Error(xhr.statusText || 'Request failed'));
}
};
xhr.onerror = () => options.error?.(new Error('Network Error'));
// 8. 发送
xhr.send(options.data || null);
// 返回可中止的对象
return { abort: () => xhr.abort() };
}
5. 上传文件 + 进度条(唯一 fetch 至今仍麻烦的场景)
const fileInput = document.querySelector('input[type=file]');
const progress = document.querySelector('progress');
fileInput.onchange = () => {
const file = fileInput.files[0];
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
progress.value = (e.loaded / e.total) * 100;
}
};
xhr.onload = () => alert('上传成功!');
xhr.onerror = () => alert('上传失败');
const form = new FormData();
form.append('avatar', file);
xhr.send(form);
};
6. XHR vs fetch 终极对比(2025 年版)
特性XMLHttpRequestfetch()胜者上传进度原生支持(xhr.upload)需 ReadableStream 手动实现XHR下载进度原生 onprogress需手动解析 body streamXHR超时控制原生 timeout需 AbortController + setTimeoutXHR中止请求abort()AbortController平手同步请求支持(不推荐)完全不支持XHR自动解析 JSON需手动 JSON.parseresponse.json()fetch流式处理不支持原生支持fetch跨域带 cookiewithCredentialscredentials: ‘include’平手兼容性IE11+IE 全灭,现代浏览器全支持XHR(老项目)代码简洁性复杂极简fetch
7. 总结:什么时候还得用 XHR?
场景必须用 XHR?需要显示上传进度条Yes大文件分片上传Yes老项目维护(jQuery/AngularJS)Yes需要同步请求(极少数场景)Yes企业内部系统(IE11 兼容)Yes日常 CRUD、JSON 接口推荐 fetch
一句话定论:
fetch 是未来,XHR 是现在和过去。学新项目用 fetch,面试 + 维护老项目 + 实现上传进度条 → 必须精通 XMLHttpRequest!
记住这张图,你就永远不会被 XHR 面试题难倒:
open() → setRequestHeader() → send() → onprogress → onload/onerror/ontimeout → abort()
