Ajax(Asynchronous JavaScript and XML)是 Web 2.0 时代的核心技术。它让 JS 可以在不刷新页面的情况下,主动向服务器发起 HTTP 请求、获取数据、动态更新页面。

没有 Ajax 之前,任何数据更新都要整页刷新 —— 体验极差。Ajax 让 “单页应用(SPA)” 成为可能。


1. 数据序列化:JSON.stringify

网络传输的是二进制文本,JS 对象不能直接发送,需要序列化为 JSON 字符串。

// 服务端:把 JS 对象序列化成 JSON 字符串,发给客户端
const todo = [
  { id: 1, title: '过四六级', completed: false },
  { id: 2, title: '回家过节', completed: false }
];

// JSON.stringify(value, replace?, space?)
// replace: 过滤/替换,传 null 表示原样序列化
// space: 缩进空格数,团队规范,提升可读性
const jsonStr = JSON.stringify(todo, null, 2);
// 结果:格式化的 JSON 字符串,可读性好

三个参数

参数 说明
value 要序列化的 JS 对象
replace? 过滤/替换函数或数组,null 表示原样输出
space? 缩进空格数,推荐 2,团队规范,可读性好

客户端收到后,用 JSON.parse() 还原:

const todo = JSON.parse(xhr.responseText); // JSON 字符串 → JS 对象

2. JS 异步处理

JS 是单线程语言 —— 同一时间只能做一件事。遇到耗时操作(网络请求、定时器、文件读写),不能卡在那里等,必须用异步机制来处理。

2.1 事件循环(Event Loop)

同步代码 → 全部执行完
异步任务 → 放入 Event Loop 队列
等同步代码跑完 → 从 Event Loop 里拿出回调函数执行
console.log('1. 开始');

setTimeout(() => {
  console.log('3. 定时器回调'); // 异步,进入 Event Loop
}, 0);

console.log('2. 结束');

// 输出顺序:1 → 2 → 3
// 即使 setTimeout 设了 0ms,也要等同步代码全部执行完

2.2 三种异步写法

JS 异步处理经历了三代演进:

方式 年代 特点
回调函数(Callback) 古老 嵌套深了变成"回调地狱",难以维护
Promise + then() ES6 链式调用,解决了回调地狱
async / await ES2017 最推荐,写法跟同步代码一样,可读性极佳
// 方式一:回调函数 — 最原始
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    // 拿到数据后的处理...
  }
};

// 方式二:Promise + then() — 好一些
fetch('/todo')
  .then(res => res.json())
  .then(data => {
    // 拿到数据后的处理...
  })
  .catch(err => console.error(err));

// 方式三:async / await — 最推荐!
const response = await fetch('/todo');
const data = await response.json();
// 跟同步代码看起来一模一样,清晰直观

3. 后端:Node.js HTTP 服务器

用 Node.js 内置的 http 模块创建一个简单的 API 服务器。

// backend/index.js
// node 内置的 http 模块,用于创建 http 服务器
// require 是 Node.js 早期的模块化系统 CommonJS
// EMS 是升级版:import + export default
const http = require('http');

// 伺服状态 — http 基于请求-响应模型
http.createServer((req, res) => {
  const todo = [
    { id: 1, title: '过四六级', completed: false },
    { id: 2, title: '回家过节', completed: false }
  ];

  // 设置 CORS 响应头,允许跨域访问
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Content-Type', 'application/json;charset=utf-8');

  // req.url — 用户请求的路径
  if (req.url === '/') {
    res.end('hello world');
  }

  if (req.url === '/todo') {
    // 序列化为 JSON 字符串后返回
    res.end(JSON.stringify(todo));
  }
}).listen(3000, () => {
  console.log('server is running on 3000 port');
});

启动:

node backend/index.js
# 输出: server is running on 3000 port

4. 前端:从 XHR 到 Fetch

4.1 XMLHttpRequest(经典方式)

XMLHttpRequest(XHR)是 Ajax 的底层实现,fetch 的"前辈"。

<!-- frontend/index.html -->
<body>
  <ul id="todo"></ul>
  <button id="btn">获取todo</button>

  <script>
    document.getElementById('btn')
      .addEventListener('click', () => {
        // 1. 实例化 XHR 对象
        const xhr = new XMLHttpRequest();

        // 2. 打开 HTTP 通道(第三个参数 true = 异步)
        xhr.open('GET', 'http://localhost:3000/todo', true);

        // 3. 监听响应(回调函数 callback)
        xhr.onreadystatechange = function () {
          // readyState: 0→1→2→3→4
          if (xhr.readyState === 4 && xhr.status === 200) {
            const todo = JSON.parse(xhr.responseText);
            document.getElementById('todo')
              .innerHTML = todo
                .map(item => `<li>${item.title}</li>`)
                .join('');
          }
        };

        // 4. 发送请求
        xhr.send();
      });
  </script>
</body>

XHR readyState 状态码

readyState 含义
0 UNSENT — 还没调用 open
1 OPENED — 已调用 open
2 HEADERS_RECEIVED — 已收到响应头
3 LOADING — 正在接收响应体
4 DONE — 请求完成

4.2 Fetch + async/await(现代方式)

fetch 是 XHR 的现代替代品,基于 Promise,配合 async/await 使用体验极佳。

<!-- frontend/fetch.html -->
<body>
  <ul id="todo"></ul>
  <button id="btn">获取todo(Fetch版)</button>

  <script>
    document.getElementById('btn')
      .addEventListener('click', async () => {
        try {
          // fetch 返回 Promise,await 等它完成
          const response = await fetch('http://localhost:3000/todo');
          const todos = await response.json();

          document.getElementById('todo')
            .innerHTML = todos
              .map(todo => `<li>${todo.title}</li>`)
              .join('');
        } catch (error) {
          console.error('请求失败:', error);
        }
      });
  </script>
</body>

对比 XHR:fetch 代码量少了近一半,逻辑更线性、更易读,错误处理也更优雅。


5. 技术演进路线

XHR(回调地狱)
  ↓
fetch + Promise.then(链式调用)
  ↓
fetch + async/await(推荐,像同步代码一样写异步逻辑)

核心要点async/await 是目前最优秀的异步处理方式 — 看起来和同步代码一样直观,但底层依然是异步、非阻塞的。


6. 文件结构

ajax/
├── readme.md              ← 本文
├── backend/
│   ├── package.json
│   └── index.js           ← Node.js HTTP 服务器
└── frontend/
    ├── index.html         ← XHR 经典版
    └── fetch.html         ← Fetch + async/await 现代版
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐