JS中XMLHttpRequest (XHR) 发送HTTP请求的步骤详解

作者:青柠

时间:2026-11-03

浏览:634

深入讲解JavaScript中使用XMLHttpRequest (XHR) 发送HTTP请求的完整步骤、生命周期状态、GET与POST数据封装差异、异常排错及核心最佳实践。

文章目录
标签:JavaScript / 网络通信 对象:XMLHttpRequest (XHR) 阅读时长:约 8 分钟 代码规范:ES6+ / 原生通信标准
JS中XMLHttpRequest (XHR) 发送HTTP请求的步骤详解
在前端开发或维护老旧工程时,即便有现代 Fetch API 和各类第三方 Axios 类库,原生 XMLHttpRequest(简称 XHR)依然是深入理解浏览器异步网络传输、精准控制上传进度与排查底层 HTTP 问题的坚实基石。发送一个标准的 XHR 请求遵循严密的时序逻辑,包含:创建实例、初始化配置、注册监听回调、配置报头、发送数据体五个基本步骤。本文将结合代码实现、状态机流转与结构对比,清晰拆解每一个关键环节。

一、XHR 发送 HTTP 请求的标准五大步骤

为了保证网络连接可靠建立并不丢失服务端推送的事件通知,浏览器对 XMLHttpRequest 的调用顺序有明确要求。下面按照开发中最规范的执行路径逐步展开。

1 创建 XMLHttpRequest 实例

在向服务器发出请求前,首先要在当前执行上下文中实例化一个通信对象:

const xhr = new XMLHttpRequest();

执行该语句后,浏览器在内存中分配与当前请求关联的通道句柄,此时对象的 readyState 状态属性初始为 0(对应常量 XMLHttpRequest.UNSENT),网络通道尚未被激活。

2 调用 open() 初始化请求参数

使用 xhr.open() 方法指定本次通信的 HTTP 动词、目标地址以及是否异步:

xhr.open(method, url, async, user, password);
  • method:请求方法,如 'GET'、'POST'、'PUT'、'DELETE'(建议大写)。
  • url:目标请求路径,可为相对路径(如 /api/data)或绝对 URL。
  • async:布尔值,是否异步执行。默认值为 true。严禁在生产主线程将其设为 false(同步会冻结 UI 渲染与交互)。
  • user / password:可选参数,用于 HTTP 基本身份认证。

调用 open() 成功后,readyState 立即跃迁为 1(XMLHttpRequest.OPENED)。此时请求并未向网络发出,仅在内部完成上下文初始化。

3 注册响应事件与状态监听器

虽然技术上在调用 send() 之前绑定事件均可,但最佳实践是紧随 open() 之后立即注册监听函数,以防某些同源缓存响应在触发瞬间被遗漏。

现代浏览器标准推荐直接使用 xhr.onload 与 xhr.onerror,或传统通用的 xhr.onreadystatechange:

// 方式 A:现代化事件监听(推荐)
xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('请求成功:', xhr.response);
  } else {
    console.error('服务端返回异常状态:', xhr.status, xhr.statusText);
  }
};

xhr.onerror = function() {
  console.error('网络层故障或跨域拦截');
};

// 方式 B:经典状态机监听
xhr.onreadystatechange = function() {
  if (xhr.readyState === XMLHttpRequest.DONE) {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('数据接收完成:', xhr.responseText);
    }
  }
};
4 设置 HTTP 请求头 (可选但关键)

如果请求需要向服务端提交数据(如 JSON、表单数据),或者需要附带鉴权凭据(如 Token),必须使用 setRequestHeader():

xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.setRequestHeader('Authorization', 'Bearer eyJhbGciOi...');
⚠️ 时机限制警示

setRequestHeader() 必须在 open() 之后、且在 send() 之前调用。若在 open() 之前调用,浏览器会抛出 InvalidStateError 异常;若在 send() 之后调用,报头早已封包发送,配置将无效。

5 调用 send() 发送请求体

调用 xhr.send() 正式把请求推入浏览器网络栈,底层开始 TCP 握手并传输 HTTP 报文:

// 发送 GET 请求(无需载荷)
xhr.send(null);

// 发送 POST 请求(携带 JSON 字符串或 FormData)
const payload = { username: "developer", role: "admin" };
xhr.send(JSON.stringify(payload));

如果使用异步模式(默认),send() 方法调用后会立即返回,程序继续往下执行,不会发生线程阻塞。当服务端完成响应传输后,先前注册的回调函数便会被事件循环依次唤醒。

JS中XMLHttpRequest (XHR) 发送HTTP请求的步骤详解
图 1:XMLHttpRequest 发送 HTTP 请求的完整时序与操作流程图

二、核心状态机:readyState 与事件生命周期

要写出鲁棒的通信代码,必须准确区分 readyState(XHR 自身通道状态)与 status(HTTP 响应状态码)的区别。

1. readyState 五种常量的含义

值 常量名称 触发时机 可用属性与典型操作
0 UNSENT 刚刚执行 new XMLHttpRequest() 尚未调用 open(),status 和 response 均为空
1 OPENED 成功执行 xhr.open() 可设置请求头 setRequestHeader 及 withCredentials
2 HEADERS_RECEIVED 已调用 send() 且已收到 HTTP 响应头 xhr.status 及 getResponseHeader() 已经可读取
3 LOADING 正在下载 HTTP 响应体数据 responseText 持有已收到的部分分片数据
4 DONE 请求全部完成或发生不可逆错误 完整响应数据可访问,整个网络事务闭环
💡 关键概念区分:readyState === 4 与 HTTP Status 200

readyState === 4 只代表底层网络请求完整走完了往返流程,即使服务端返回了 404 Not Found 或 500 Internal Server Error,readyState 依然会变为 4。
因此业务逻辑判定数据是否有效,必须双重确认:xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)。

JS中XMLHttpRequest (XHR) 发送HTTP请求的步骤详解
图 2:XHR readyState 状态跃迁与事件生命周期示意图

三、实战范例:GET 与 POST 请求的完整实现

下面分别给出包含参数拼接、异常捕获、响应类型转换的规范代码模板。

1. 完整 GET 请求示例(含 Query 参数编码与 responseType)

/**
 * 发送标准 GET 请求
 * @param {string} baseUrl - 目标接口基础地址
 * @param {object} params - 查询参数键值对
 */
function fetchUserData(baseUrl, params) {
  // 1. 拼接并转义 Query 查询字符串
  const queryString = Object.keys(params)
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
    .join('&');
  const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl;

  // 2. 实例化
  const xhr = new XMLHttpRequest();

  // 3. 打开连接
  xhr.open('GET', fullUrl, true);

  // 4. 优化响应解析:指定返回为 JSON 对象,无需手动 JSON.parse
  xhr.responseType = 'json';

  // 5. 设置超时时间(单位:毫秒)
  xhr.timeout = 8000;

  // 6. 注册事件监听
  xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('获取数据成功:', xhr.response);
    } else {
      console.error('业务失败,状态码:', xhr.status);
    }
  };

  xhr.ontimeout = function() {
    console.error('网络请求超时,耗时超过 8000ms');
  };

  xhr.onerror = function() {
    console.error('发生网络底层错误或跨域违规');
  };

  // 7. 发送请求(GET 传 null)
  xhr.send(null);
}

// 调用示例
fetchUserData('https://api.example.com/users', { page: 1, pageSize: 20 });

2. 完整 POST 请求示例(提交 JSON 数据载荷)

/**
 * 发送标准 POST 请求提交 JSON 数据
 * @param {string} url - 提交地址
 * @param {object} data - 负载对象
 */
function submitOrder(url, data) {
  const xhr = new XMLHttpRequest();

  xhr.open('POST', url, true);

  // 关键步骤:设置报文头,声明请求体数据为 JSON 格式
  xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
  xhr.setRequestHeader('Accept', 'application/json');

  xhr.responseType = 'json';

  xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('订单提交成功,返回数据:', xhr.response);
    } else {
      console.error('提交失败:', xhr.status, xhr.statusText);
    }
  };

  xhr.onerror = function() {
    console.error('提交发生网络错误');
  };

  // 将 JavaScript 对象序列化为 JSON 字符串并压入请求体
  const bodyPayload = JSON.stringify(data);
  xhr.send(bodyPayload);
}

// 调用示例
submitOrder('https://api.example.com/orders', {
  goodsId: 10086,
  quantity: 2,
  payChannel: 'wechat'
});
JS中XMLHttpRequest (XHR) 发送HTTP请求的步骤详解
图 3:GET 与 POST 请求在 XHR 中的传输差异与数据封装结构

四、进阶场景与常见避坑指南

1. 跨域资源共享 (CORS) 与凭据携带

当 XHR 访问不同源(协议、域名、端口任一不同)的资源时,浏览器安全机制会触发同源策略检查:

  • 基本跨域:服务端必须在响应头中包含 Access-Control-Allow-Origin: * 或指定来源站点。否则客户端触发 onerror,status 为 0,在控制台打印 CORS 拦截提示。
  • 携带 Cookie 与身份认证:默认情况下,跨域 XHR 不会发送 Cookie。若要携带 Cookie,需在调用 send() 之前设置:
    xhr.withCredentials = true;
    此时服务端响应头必须明确指定具体域名,且附带 Access-Control-Allow-Credentials: true,绝不能配置为通配符 *。

2. 文件上传与进度监控 (XHR Level 2)

XHR 相比原始 Fetch 的一大优势在于对上传进度的原生支持。在 xhr.upload 对象上可监听 onprogress:

const formData = new FormData();
formData.append('file', fileInput.files[0]);

const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload', true);

// 监听上传进度
xhr.upload.onprogress = function(event) {
  if (event.lengthComputable) {
    const percentComplete = Math.round((event.loaded / event.total) * 100);
    console.log(`上传进度: ${percentComplete}%`);
  }
};

// 注意:上传 FormData 时严禁手动设置 Content-Type,让浏览器自动补充 boundary 分界字符串!
xhr.send(formData);

3. 请求超时控制与手动中止

在用户快速切换页面、Tab 标签或输入自动补全时,未完成的请求应及时丢弃以节省带宽:

// 随时调用 abort() 中止尚未完成的请求
xhr.abort(); // 此时 readyState 重置为 0,且触发 onabort 回调
JS中XMLHttpRequest (XHR) 发送HTTP请求的步骤详解
图 4:XMLHttpRequest 核心 API 属性、方法与异常排错全景总结

五、结语与总结

熟练掌握 XMLHttpRequest 的五大操作步骤(new 实例化 → open 配置 → onload 监听 → setRequestHeader 报头 → send 投递)及其背后的状态机运作机制,不仅是应对老旧项目维护与高精细度网络任务(如带进度的断点续传、底层报文探针)的必备技能,更能帮助前端开发者从根本上透彻理解现代 Fetch、Axios 乃至全栈 HTTP 通信库的演化逻辑。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章