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 默认 true
send(body?)发送请求,body 可以是 string、Document、Blob、FormData 等
setRequestHeader(name, value)设置请求头(必须在 open() 之后、send() 之前)
abort()中止请求,触发 onabort
getResponseHeader(name)获取单个响应头
getAllResponseHeaders()获取所有响应头(字符串,\r\n 分隔)
属性readyState0–4 五个状态
status / statusTextHTTP 状态码和文字
response响应体(根据 responseType 自动解析)
responseText永远是字符串(即使出错)
responseXML如果是 XML 且响应头正确,会自动解析为 Document
responseURL最终重定向后的 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()
文章已创建 2732

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部