JS-面试必考:手动实现一个标准的 Ajax 请求(XMLHttpRequest)
前言
虽然 fetch API 在现代开发中非常流行,但 XMLHttpRequest (XHR) 依然是理解 Web 网络编程的基石。无论是底层的 Ajax 封装,还是需要细粒度监控上传/下载进度的场景,XHR 依然不可替代。
一、 什么是 Ajax?
Ajax(Asynchronous JavaScript And XML) 并不是一种单一的编程语言,而是一套技术的组合。其核心是在不重新加载整个网页的情况下,通过与服务器进行异步数据交换,实现页面的局部刷新。
- 核心优势:提升用户体验,减少网络带宽占用。
-
实现基础:JavaScript 和浏览器原生的
XMLHttpRequest对象。
二、 XHR 操作的“五步法”逻辑
1. 创建对象
let xhr = new XMLHttpRequest();
2. 注册回调函数 (事件驱动)
XHR 是基于事件驱动的,我们需要在请求发送前定义好如何处理各种状态。
-
onreadystatechange:监控请求的生命周期(状态 0-4)。 -
onerror:处理网络层面的错误(如断网或 DNS 解析失败)。 -
ontimeout:处理响应超时,需配合xhr.timeout使用。 -
onprogress:数据传输进度监控。
3. 配置请求参数
通过 open() 方法初始化请求:
-
method:请求方法(GET, POST, PUT 等)。 -
url:请求地址。 -
async:是否异步(默认true)。注意: 设为false会阻塞浏览器主线程,导致页面假死。
4. 设置属性与请求头
-
responseType:设置期望的返回格式(如json,blob)。 -
setRequestHeader():手动添加自定义请求头(必须在open之后,send之前调用)。
5. 发送请求
-
send(data):正式发起请求。如果是 GET 请求,传null;如果是 POST,传具体数据。
三、 详解 readyState:五种状态的演变
readyState 是排查 Ajax 问题的关键,它记录了请求的每一步进展:
| 取值 | 状态名称 | 描述 |
|---|---|---|
| 0 | UNSENT | 初始化状态,对象已创建,尚未调用 open()
|
| 1 | OPENED | 已调用 open(),尚未调用 send(),可设置 Header |
| 2 | HEADERS_RECEIVED | 已调用 send(),接收到了响应头和状态码 |
| 3 | LOADING | 正在下载响应体,responseText 已包含部分数据 |
| 4 | DONE | 数据接收完成,请求生命周期结束 |
四、 进度监控:如何实现进度条?
XHR 相比 fetch 的一大优势就是对进度的原生支持。通过 onprogress 事件,我们可以计算出下载百分比:
-
lengthComputable:布尔值,表示服务器是否返回了Content-Length。 -
loaded:已接收的字节数。 -
total:总字节数。
五、 代码实现:封装一个标准的 Ajax 函数
/**
* 基于 XHR 封装的数据获取函数
* @param {string} URL 请求地址
*/
function GetWebData(URL) {
const xhr = new XMLHttpRequest();
// 1. 状态监控:只在 DONE 阶段处理逻辑
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 兼容性判断:2xx 范围和 304 缓存均认为成功
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log("请求成功:", xhr.response);
} else {
console.error("请求失败, 状态码:", xhr.status);
}
}
};
// 2. 进度监控
xhr.onprogress = function (event) {
if (event.lengthComputable) {
let percent = (event.loaded / event.total) * 100;
console.log(`下载进度: ${percent.toFixed(2)}%`);
}
};
// 3. 异常与超时处理
xhr.timeout = 5000;
xhr.ontimeout = () => console.error("请求超时!");
xhr.onerror = () => console.error("网络异常或跨域错误");
// 4. 配置与发送
xhr.open("GET", URL, true);
xhr.responseType = "json"; // 自动解析 JSON
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.send(null);
}