从零掌握 Ajax:一次请求带你读懂异步数据加载原理
2025年11月13日 23:45
一、精炼理解:Ajax 到底是啥?
Ajax = 在不刷新页面的情况下,和服务器交换数据并动态更新页面。
它让 Web 页面拥有“应用级”的体验——局部更新、响应式交互、更少等待。核心实现长期基于浏览器内建对象 XMLHttpRequest(XHR) ,现代开发则多用 fetch / Axios,但底层思想相同:异步请求 + 数据驱动视图。
二、核心流程
-
创建 XHR 实例
const xhr = new XMLHttpRequest(); -
打开连接
xhr.open('GET', '/api/members', true); // true => 异步 -
可选:设置请求头 / 超时 / 响应类型
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.responseType = 'json'; // 直接得到对象(现代浏览器) xhr.timeout = 5000; // 毫秒 -
发送请求
xhr.send(); -
监听状态 / 处理响应
xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status === 200) { const data = xhr.responseType === 'json' ? xhr.response : JSON.parse(xhr.responseText); // 更新 DOM } else { // 处理非 200 的情况 } } }; -
清理/取消
xhr.abort(); // 主动取消请求
三、readyState 一眼看懂
-
0UNSENT:未调用open() -
1OPENED:调用了open(),可setRequestHeader、send -
2HEADERS_RECEIVED:已接收响应头(可读取 status) -
3LOADING:接收响应体中(可处理流式数据) -
4DONE:响应完成,数据就绪
调试建议:打印 readyState 与 status,快速定位在哪个阶段失败。
四、常见坑与解决方法
1. 同步请求会卡死 UI
xhr.open(..., false) 是同步,会阻塞主线程。不要在浏览器中使用同步请求,除非你非常清楚场景(几乎没有)。
2. 跨域(CORS)错误
浏览器会阻止不满足 CORS 策略的请求。后端需设置 Access-Control-Allow-Origin,或通过代理绕过开发时限制。
3. JSON 解析报错
JSON.parse 报错说明服务器没返回合法 JSON。排查响应 Content-Type、响应体首尾空白和服务器异常栈。
4. 超时与重试
设置合理 xhr.timeout,并在 ontimeout 中进行用户提示或重试逻辑(避免无限重试)。
5. 状态码不是 200
处理 4xx/5xx、304 等不同类型响应,给用户明确提示而非只在控制台打印错误。
6. 网络抖动与幂等
POST 请求若可重试需保证幂等(或在服务端做去重),避免重复提交造成的数据问题。
五、XHR 与 fetch/Axios 对比(实用速览)
| 特性 | XHR | fetch | Axios |
|---|---|---|---|
| 语法风格 | 回调/事件 | Promise(更现代) | Promise + 自动 JSON 处理 |
| 支持流 | 有(onprogress) | 有(ReadableStream) | 基于 fetch/XHR,封装更好 |
| 自动 JSON 解析 | 否 | 需 .json()
|
是(响应自动转换) |
| 超时设置 | 有(timeout) |
需 AbortController | 内置超时配置 |
| 更灵活的进度 | 是(上传/下载 progress) | 比较复杂 | 封装上传/下载进度支持 |
建议:新项目优先用
fetch或 Axios;但遇到上传进度/低层控制时,XHR 的事件模型仍然非常有用。
六、实用示例:可靠的异步请求
<ul id="members"></ul>
<script>
const ul = document.getElementById('members');
function fetchMembers() {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.github.com/orgs/lemoncode/members', true);
xhr.responseType = 'json'; // 现代浏览器直接得到对象
xhr.timeout = 7000;
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if (xhr.status === 200) {
const data = xhr.response;
ul.innerHTML = data.map(u => `<li>${u.login}</li>`).join('');
} else {
ul.innerHTML = `<li>请求失败:${xhr.status} ${xhr.statusText}</li>`;
}
};
xhr.ontimeout = () => {
ul.innerHTML = '<li>请求超时,请重试</li>';
};
xhr.onerror = () => {
ul.innerHTML = '<li>网络错误,请检查连接</li>';
};
xhr.send();
return xhr; // 若需要外部取消请求,可保留此引用并调用 xhr.abort()
}
fetchMembers();
</script>
七、总结
掌握 Ajax,不只是知道如何发请求,而是理解请求生命周期、错误处理与用户体验折衷——把网络的不确定性,优雅地交给代码去处理。