Ajax 入门:用 XHR 理解前后端异步请求

Ajax 入门:用 XHR 理解前后端异步请求

前言

Ajax 是前端开发中非常重要的概念。

它让浏览器可以在不刷新整个页面的情况下,通过 JavaScript 主动向后端请求数据,然后把返回的数据更新到页面中。

在现代开发中,我们更常使用 fetchaxios,但 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 负责解析和渲染数据

掌握这条链路后,再学习 fetchaxios、RESTful 接口和前后端分离项目,就会更容易理解。


总结

本文通过一个 TodoList 示例,梳理了 Ajax 和 XHR 的基本流程。

整体过程可以概括为:

rust 复制代码
Node 提供接口
  -> XHR 发起请求
  -> 后端返回 JSON
  -> 前端解析数据
  -> 页面动态渲染

虽然现在实际开发中更常用 fetchaxios,但 XHR 依然很适合作为理解 Ajax 原理的入口。

它帮助我们看清楚:前端页面的数据并不一定写死在 HTML 中,而是可以通过 JavaScript 从后端接口动态获取。

相关推荐
Java编程爱好者1 小时前
Java 8老系统SQL Agent实战:AI生成候选SQL,安全引擎拦截后再执行
后端
yingyima1 小时前
Python re 模块速查:从实战对比中掌握正则表达式
前端
copyer_xyf1 小时前
Agent 结构化输出
后端·python·agent
ping某1 小时前
为什么我背了很多年 TCP 三次握手,还是总觉得差一点?
后端
一个做软件开发的牛马2 小时前
Spring Boot 自动配置原理揭秘:从 @SpringBootApplication 到手写自定义 Starter
java·后端
周杰伦fans2 小时前
续集:工作空间一切换,我的插件菜单就消失?——MenuBar与Ribbon的自动重载方案
后端·ribbon·c#
放下华子我只抽RuiKe52 小时前
FastAPI 全栈后端(三):数据库与 ORM
前端·数据库·react.js·oracle·性能优化·前端框架·fastapi
源图客2 小时前
境外电商 - 龙虾智能体-综合选品推荐报告
开发语言·javascript·ecmascript
磊 子3 小时前
C++设计模式
javascript·c++·设计模式