在 Fetch 和 Axios 盛行的今天,为什么还要重学 XMLHttpRequest (XHR)?因为它是浏览器网络通信的基石。跳过底层直接学上层库,你得到的只是"术";理解 XHR,掌握的才是"道"。
本章将带你从概念到实战完成一次深度跨越:
- 具象化理解:用生活类比讲透"异步"本质,告别死记硬背。
- 掌控底层:拆解 XHR 生命周期与关键属性,提升调试排查能力。
- 破解痛点:通过回调与 Promise 两种方案,彻底解决"异步数据外部无法使用"的经典难题。
💡 阅读提示 :建议打开浏览器控制台(F12),跟着代码亲手敲一遍。只有亲眼看到状态变化、亲手拿到数据,知识才能真正内化。让我们回到原点,重构你的 Ajax 认知体系。 AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术
AJAX 通过在后台与服务器交换数据,并能够更新部分网页内容。
本次使用的编辑器为:webStorm接口模拟使用的为:Apifox
以下为mock的Json数据,大家可以复制使用:
markdown
{
"code": 200,
"message": "请求成功",
"data": {
"stationName": "华东第一光伏电站",
"inspectionDate": "2026-10-26",
"inspector": {
"id": 1001,
"name": "张三",
"role": "高级工程师"
},
"devices": [
{
"deviceId": "PV-001",
"type": "逆变器",
"status": "normal",
"metrics": {
"voltage": 380.5,
"current": 12.4,
"temperature": 45.2
},
"lastMaintenance": "2026-09-15"
},
{
"deviceId": "BAT-002",
"type": "储能电池组",
"status": "warning",
"metrics": {
"voltage": 220.1,
"current": 0.0,
"temperature": 62.8
},
"lastMaintenance": "2026-08-20",
"remark": "温度略高,建议检查散热风扇"
},
{
"deviceId": "INV-003",
"type": "汇流箱",
"status": "offline",
"metrics": null,
"lastMaintenance": "2026-10-01",
"remark": "通讯模块故障,正在维修"
}
],
"summary": {
"totalDevices": 3,
"normalCount": 1,
"warningCount": 1,
"offlineCount": 1
}
}
}
APIfox大概设置步骤:

1. 核心概念拆解
- 异步:这是Ajax的灵魂。传统的网页请求时"同步"的,即浏览器发送请求后必须等待服务器响应,期间页面会卡顿或刷新。而Ajax允许浏览器在后台与服务器交换数据,用户在此期间可以继续操作页面,不会感到终端。
- JavaScript:用于发送请求和处理返回的数据。
- XML:最初Ajax使用XML格式传输数据,但是现在常用的是
JSON,因为它更轻量、更易解析。
2. Ajax的工作原理
想象一下你去餐厅吃饭:
- 传统方式:你点完菜,必须站在柜台前一直等着,直到厨师做好菜端给你,你才能回到座位吃,这期间你没有办法去做别的事情
Ajax方式(异步) :你点完菜后,回到座位继续聊天、喝水(继续浏览网页)。厨师做好菜后,服务员会把菜悄悄端到你桌上(后台更新数据),你不需要离开座位,也不需要重启整个用餐过程。
2.1. Ajax 的工作流程
-
触发事件
用户在网页上进行某种操作,例如:
- 点击按钮
- 输入框内容改变
- 页面滚动到特定位置
- 定时器触发
-
创建请求对象
JavaScript会创建一个用于与服务器通信的对象。
- 传统方式:使用
XMLHttpRequest对象。 - 现在方式:使用
fetch()函数或第三方库如Axios
- 传统方式:使用
-
配置并发送请求
开发者需要配置请求的细节,然后发送给服务器:
- 请求方法
method:GET(获取数据)、POST(提交数据)、Put、DELETE等。 - 请求地址
URL:服务器的API接口地址。 - 请求头
Headers:设置内容类型、认证令牌等。 - 请求体
Body:如果POST请求,需要携带发送给服务器的数据(通常是JSON格式)。
- 请求方法
-
服务器处理
服务器接收到请求后:
- 解析请求参数。
- 执行相应的业务逻辑(如查询数据库、计算数据)。
- 生成响应数据(通常是
JSON或XML格式)。 - 返回
HTTP状态码(如200 OK,404 Not Found,500 Internal Server Error)。
-
接收响应
浏览器在后台接收到服务器的响应。
注意:此时页面不会刷新,用户可以继续操作其他内容。
-
处理响应数据
JavaScript 通过回调函数、Promise 的
.then()或async/await来处理响应:- 检查状态码 :确认请求是否成功(如判断
status === 200)。 - 解析数据 :将返回的
JSON字符串转换为JavaScript对象。 - 错误处理:如果请求失败,执行相应的错误逻辑(如显示提示框)。
- 检查状态码 :确认请求是否成功(如判断
-
更新
DOM最后,JavaScript 使用解析后的数据来更新页面的特定部分:
- 修改元素的文本内容 (
innerText)。 - 插入新的 HTML 元素 (
innerHTML)。 - 修改样式或属性。
- 修改元素的文本内容 (
3. XHR(XMLHttpRequest)
XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新.
3.1 XHR的核心生命周期
| 状态 | 常量名 | 描述 |
|---|---|---|
| 0 | UNSENT |
代理被创建,但尚未调用 open() 方法。 |
| 1 | OPENED |
open() 方法已被调用,准备发送请求。 |
| 2 | HEADERS_RECEIVED |
send() 方法已被调用,且已接收到响应头(Headers)。 |
| 3 | LOADING |
正在下载响应体(Response Body),responseText 中可能已有部分数据。 |
| 4 | DONE |
请求操作已完成。无论成功还是失败,此时可以获取完整的响应数据。 |
在实际开发中我们一般关注状态4,因为这时的数据才能正常使用
3.2 标准的工作流程
一个完整的 XHR 请求包含以下五个核心步骤:
3.2.1. 实例化对象
ini
const xhr = new XMLHttpRequest();
3.2.2. 初始化请求
open(method,url,async)方法用于配置请求的基本信息。
method:http方法,如GET,POST,PUT,DELETE。url:请求的目标地址。async:true:表示异步(默认 );false:表示同步(严禁使用,会阻塞主线程导致页面卡死)
kotlin
xhr.open('POST', 'https://api.example.com/login', true);
3.2.3. 设置请求头
在 open 之后、send() 之前调用。对于 POST 请求,通常需要告诉服务器发送的数据格式。
js
// 告诉服务器我们要发送 JSON 数据
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
// 如果需要身份认证,通常会在这里添加 Token
xhr.setRequestHeader('Authorization', 'Bearer your_token_here');
3.2.4. 监听状态变化
这是处理响应的核心。由于是异步,我们需要一个回调函数来监听状态的改变。
js
xhr.onreadystatechange = function() {
// 只有当 readyState 为 4 且 status 为 200 时,才代表请求成功
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
console.log('成功:', xhr.responseText);
// 处理业务逻辑...
} else {
console.error('错误:', xhr.status, xhr.statusText);
}
}
};
3.2.5. 发送请求
- GET 请求 :
send()括号内通常为空或null。 - POST 请求 :括号内传入要发送的数据字符串。如果是
JSON,需要先序列化。
js
const data = JSON.stringify({ username: 'admin', password: '123' });
xhr.send(data);
3.2.6. 完整XHR的请求实例
js
//创建xhr对象
const xhr = new XMLHttpRequest();
//初始化请求
xhr.open("GET", 'https://m1.apifoxmock.com/m1/6778385-6490725-default/api/learnajax', true)
//监听状态变化
xhr.onreadystatechange = () => {
if(xhr.readyState===4){
if(xhr.status >= 200 && xhr.status <300) {
let res = JSON.parse(xhr.responseText)
console.log('成功:',res)
}else{
console.log('错误:',xhr.status,xhr.statusText)
}
}
}
//发送请求
xhr.send()
3.3 xhr对象的属性&&如何使用响应到的数据
通过以上的方式确实可以拿到对象形式的的响应数据,但是只能在这个响应中进行数据的操作,在外部是没有办法操作这些数据的,所以我们需要采用一些方法来使响应到的数据可以方便我们外部使用。
在这之前我需要和大家说一下,需获得来自服务器的响应,请使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性。
responseText:获得字符串形式的响应数据。responseXML:获得 XML 形式的响应数据。
除此之外,还需要了解一些关于 xhr 对象的属性:
3.3.1 核心状态属性
-
readyState:4:- 含义:请求的当前阶段
- 解读:
4代表DONE(完成)。意味着整个请求过程已经结束(无论成功还是失败),数据已经接收完毕。
-
status:200:- 含义:HTTP 状态码。
- 解读:
200代表OK(成功)。服务器正常处理了请求并返回了数据。如果是404就是没找到,500就是服务器报错。
-
statusText: ""- 含义:HTTP 状态文本。
- 解读 :通常对应 status 的描述,比如 "OK" 或 "Not Found"。这里为空字符串可能是因为 Mock 服务器返回时未设置,或者浏览器解析差异,不影响逻辑,主要看
status数字即可。
3.3.2. 响应数据属性
-
responseText: "{"code":200...":- 含义:服务器返回的原始文本数据(字符串格式)。
- 解读 :这是最基础的属性。因为你的数据是
JSON格式,所以它看起来像一串带转义字符的字符串。你需要用JSON.parse(xhr.responseText)把它变成JS对象才能方便使用。
-
response: {"code":200...:- 含义:解析后的响应体。
- 解读 :注意看,这个属性里的内容已经是对象格式 了(没有外层引号)。这是因为现代浏览器比较智能,如果你设置了
xhr.responseType = 'json',或者浏览器自动推断出是JSON,它会直接帮你解析好放在这里。建议优先使用xhr.response而不是responseText,可以省去parse的步骤。
-
responseType: "":- 含义:你期望的响应类型。
- 解读 :空字符串表示默认值(通常是文本)。如果你显式设置了
xhr.responseType = 'json',这里就会显示'json'。
-
responseXML: null:- 含义:XML 格式的响应数据。
- 解读 :因为你的接口返回的是
JSON不是 XML,所以这里是null。这个属性现在很少用了。
-
responseURL:- 含义:最终响应的 URL。
- 解读:如果请求发生了重定向(Redirect),这里会显示重定向后的真实地址。
3.3.3. 事件回调属性 (监听器)
这些属性目前都是 null 或函数,代表你绑定的事件处理程序。
-
onreadystatechange: () => ...:- 解读 :这就是你代码里写的
xhr.onreadystatechange = () => {...}。因为它被赋值了,所以显示为一个箭头函数。
- 解读 :这就是你代码里写的
-
onload,onerror,ontimeout,onabort等:-
解读 :这些都是
XHR提供的其他事件钩子。onload: 请求成功完成时触发(比readyState更好用)。onerror: 网络错误时触发。onprogress: 下载进度变化时触发。
-
因为你代码里没写这些,所以它们显示为
null。
-
3.3.4. 其他配置与内部属性
-
withCredentials: false:- 含义:跨域请求是否携带 Cookie/认证信息。
- 解读 :
false表示不携带。如果你的接口需要登录态(Cookie),通常需要设为true。
-
timeout: 0:- 含义:超时时间(毫秒)。
- 解读 :
0表示永不超时(直到浏览器自己断开)。
-
upload:- 含义:上传进度对象。
- 解读 :用于监听文件上传的进度(
xhr.upload.onprogress)。
-
[[Prototype]]:- 含义:原型链。
- 解读 :这是
JS对象的底层结构,指向XMLHttpRequest.prototype,不用太关注。
3.3.5 如何去使用响应到的数据
上边就是xhr对象中的一些属性,建议大家理解即可,不用太执着某一点。接下来就一起来看一下如何去使用或者说是处理响应到的数据,首先响应到的数据只能在如下区域使用:
也就是说这部分代码会非常的冗余,臃肿,不易读。那么我们可定是更建议在外部去处理得到的数据。那么我这里提供两种方式,不是最好,但是好用,一个是通过回调函数取出数据,供外部使用,另一种是通过promise的方式。接下来就一个一个的演示。
3.3.5.1. 封装成函数并传入"回调函数"的方式
js
/**
* 获取测试数据
* @param url 请求的地址
* @param callback 供内部调用,把数据传出,供外部使用
*/
function getAjaxData(url,callback) {
const xhr = new XMLHttpRequest();
xhr.open("GET", url, true)
xhr.onreadystatechange = () => {
if(xhr.readyState===4){
if(xhr.status >= 200 && xhr.status <300) {
let res = JSON.parse(xhr.responseText)
callback(null,res)
}else{
callback(new Error("请求失败"),null)
console.log('错误:',xhr.status,xhr.statusText)
}
}
}
xhr.send();
}
//使用
const Url = 'https://m1.apifoxmock.com/m1/6778385-6490725-default/api/learnajax';
getAjaxData(Url,(err,data)=>{
if (err) {
console.error(err);
} else {
console.log('我在外部拿到了数据:', data);
}
})
3.3.5.2. 使用Promise封装的方式
js
/**
* 使用promise的方式
* @param url 请求的地址
* @returns {Promise<unknown>} 通过创建promise来获取
*/
function fetchWithXHR(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
}else {
reject(new Error(`HTTP error! status: ${xhr.status}`));
}
}
};
xhr.send();
});
}
// 使用方式:配合 async/await 使用,看起来就像在等数据回来一样
async function loadData() {
try {
const data = await fetchWithXHR('https://m1.apifoxmock.com/m1/6778385-6490725-default/api/learnajax');
console.log('拿到数据了,可以在这里随意使用:', data);
} catch (error) {
console.error('出错了:', error);
}}
loadData();
4. 总结&&结语
4.1 总结
在本章中,我们完成了对 Ajax 技术从"概念认知"到"底层实战"的深度拆解。你不仅理解了 Ajax "异步交互、局部刷新"的核心灵魂,更通过 XMLHttpRequest (XHR) 这一经典 API,亲手触摸到了浏览器网络通信的脉搏。
我们重点掌握了以下核心技能:
- 生命周期掌控 :深入理解了
readyState的五个阶段,明白了为何状态4是数据处理的黄金节点。 - 完整请求链路 :从实例化、配置请求头、监听状态变化到发送请求,构建了标准的
XHR工作流。 - 响应数据解析 :学会了区分
responseText(原始字符串)与response(自动解析对象),并了解了status、withCredentials等关键属性的实际意义。 - 异步数据突围 :突破了"回调函数内部才能使用数据"的限制,掌握了回调函数封装 与
Promise + async/await两种现代化数据提取方案,为后续编写可维护的代码打下基础。
4.2. 结语
XHR 虽然诞生于 Web 早期,但它所蕴含的异步编程思想 和HTTP 交互模型 至今仍是所有现代前端框架(如 Vue、React)及网络库(如 Axios、Fetch)的基石。
学习建议: 不要将
XHR视为"过时的老古董",而应将其作为理解浏览器底层机制的"解剖标本"。当你能熟练用 Promise 封装一个健壮的XHR请求时,你就已经具备了阅读Axios源码、理解微前端通信原理以及排查复杂网络问题的底层能力。
在接下来的章节中,我们将带着这份对底层的敬畏,迈向更简洁、更强大的 Fetch API 与 Axios 实战,让异步交互变得更加优雅高效。请保持这份探索欲,因为真正的进阶,往往始于对基础的极致理解。


