Ajax 入门:用 XHR 理解前后端异步请求
前言
Ajax 是前端开发中非常重要的概念。
它让浏览器可以在不刷新整个页面的情况下,通过 JavaScript 主动向后端请求数据,然后把返回的数据更新到页面中。
在现代开发中,我们更常使用 fetch 或 axios,但 XMLHttpRequest 是 Ajax 早期的核心实现。理解 XHR,有助于我们看清前后端异步请求的基本流程。
本文用一个简单的 TodoList 示例,梳理 Ajax 从请求到渲染的完整过程。
一、Ajax 解决了什么问题?
传统页面交互中,如果想获取新数据,往往需要刷新整个页面。
而 Ajax 的核心价值是:
页面不刷新,数据可以更新。
它的基本流程是:
rust
前端发送请求
-> 后端返回数据
-> 前端解析数据
-> 页面局部更新
这样用户体验会更流畅,也让前后端分离开发成为可能。
二、后端:提供 JSON 接口
后端可以使用 Node.js 内置的 http 模块创建一个简单服务:
ini
const http = require("http");
const todos = [
{
id: 1,
title: "学习 Ajax",
completed: false
},
{
id: 2,
title: "理解 XHR",
completed: false
}
];
http.createServer((req, res) => {
if (req.url === "/todos") {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Content-Type", "application/json;charset=utf-8");
res.end(JSON.stringify(todos));
return;
}
res.end("hello world");
}).listen(3000, () => {
console.log("server is running on 3000 port");
});
这里完成了几件事:
- 创建 HTTP 服务
- 监听
3000端口 - 当访问
/todos时返回 TodoList 数据 - 使用
JSON.stringify把数组转成 JSON 字符串
接口访问地址是:
bash
http://localhost:3000/todos
返回的数据类似:
json
[
{
"id": 1,
"title": "学习 Ajax",
"completed": false
},
{
"id": 2,
"title": "理解 XHR",
"completed": false
}
]
三、为什么要用 JSON?
前后端通信时,数据需要以字符串形式在网络中传输。
所以后端通常会使用:
javascript
JSON.stringify(todos)
把 JavaScript 数组或对象转成 JSON 字符串。
前端拿到数据后,再使用:
javascript
JSON.parse(xhr.responseText)
把 JSON 字符串重新转回 JavaScript 数据。
可以简单理解为:
javascript
后端:对象 / 数组 -> JSON 字符串
前端:JSON 字符串 -> 对象 / 数组
四、前端:使用 XMLHttpRequest 请求接口
前端页面中先准备一个容器:
bash
<ul id="todos"></ul>
然后通过 XHR 请求后端接口:
ini
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:3000/todos", true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const todos = JSON.parse(xhr.responseText);
document.getElementById("todos").innerHTML =
todos.map(todo => `<li>${todo.title}</li>`).join("");
}
};
xhr.send();
这段代码可以分成四步理解。
五、XHR 的基本流程
1. 创建请求对象
ini
const xhr = new XMLHttpRequest();
这一步创建了一个 XHR 对象,用来和后端进行通信。
2. 配置请求
kotlin
xhr.open("GET", "http://localhost:3000/todos", true);
open 方法用于配置请求。
三个参数分别是:
bash
请求方法:GET
请求地址:http://localhost:3000/todos
是否异步:true
第三个参数为 true,表示这是一个异步请求。
3. 监听请求状态
ini
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// 处理响应结果
}
};
XHR 请求过程中,readyState 会发生变化。
常见状态如下:
arduino
0:未初始化
1:已调用 open
2:已收到响应头
3:正在接收响应体
4:请求完成
所以需要判断:
ini
xhr.readyState === 4
表示请求已经完成。
同时还要判断:
ini
xhr.status === 200
表示 HTTP 请求成功。
4. 发送请求
ini
xhr.send();
send 方法会真正把请求发送出去。
六、把数据渲染到页面
接口返回后,前端拿到的是字符串:
xhr.responseText
需要先解析:
ini
const todos = JSON.parse(xhr.responseText);
然后用 map 生成列表:
ini
todos.map(todo => `<li>${todo.title}</li>`)
map 返回的是数组,因此通常还要加上:
bash
.join("")
把数组拼接成完整的 HTML 字符串。
最终渲染代码是:
ini
document.getElementById("todos").innerHTML =
todos.map(todo => `<li>${todo.title}</li>`).join("");
完整数据流如下:
rust
后端数组
-> JSON.stringify
-> HTTP 响应
-> xhr.responseText
-> JSON.parse
-> 前端数组
-> map 生成 HTML
-> innerHTML 渲染页面
七、异步请求的特点
XHR 是异步请求,所以浏览器发送请求后,不会一直等待接口返回,而是会继续执行后面的代码。
例如:
arduino
console.log("start");
xhr.send();
console.log("end");
输出顺序通常是:
sql
start
end
等接口返回后,才会执行 onreadystatechange 中的回调逻辑。
这就是异步请求的特点:
先执行同步代码;
异步任务完成后,再执行回调函数。
八、Ajax 的核心理解
Ajax 不是单独指某一个 API,而是一种前后端交互方式。
它的核心是:
前端不刷新页面,主动请求后端数据,并动态更新页面。
在这个过程中:
- 后端负责提供接口
- JSON 负责数据传输
- 前端负责请求接口
- JavaScript 负责解析和渲染数据
掌握这条链路后,再学习 fetch、axios、RESTful 接口和前后端分离项目,就会更容易理解。
总结
本文通过一个 TodoList 示例,梳理了 Ajax 和 XHR 的基本流程。
整体过程可以概括为:
rust
Node 提供接口
-> XHR 发起请求
-> 后端返回 JSON
-> 前端解析数据
-> 页面动态渲染
虽然现在实际开发中更常用 fetch 或 axios,但 XHR 依然很适合作为理解 Ajax 原理的入口。
它帮助我们看清楚:前端页面的数据并不一定写死在 HTML 中,而是可以通过 JavaScript 从后端接口动态获取。