本节摘要:Ajax(Asynchronous JavaScript and XML)是一套让网页在不刷新整页的前提下与服务器通信、并只更新页面局部区域的技术组合。名字里的 XML 早已名存实亡,今天的事实标准是 JSON。理解 Ajax 的关键不是记住某个 API,而是吃透"异步通信"与"局部更新"这两个词各自解决了什么问题。
先看一个具体的现场。某电商页面上有 40 个商品、一个购物车图标,图标右上角有个红色数字,显示车里已有 3 件商品。用户点了某个商品行的"加入购物车"按钮。
传统写法里,这个按钮是一个真正的表单提交按钮。浏览器抛弃当前整个页面,向服务器发一个请求;服务器把"加入购物车"这件事记下来,然后重新渲染整张商品列表页的 HTML 返回;浏览器解析这份新 HTML,从零画一遍页面。用户看到的现象是:屏幕白一下(或灰一下),滚动位置跳回顶部,刚才看到第 37 行,现在得重新滚下去确认。
整个交互里,真正变化的信息有多少?一个数字:3 变成 4。为了这 1 个字节的业务变更,付出的代价是重新传输一整张页面(可能几百 KB)、重新解析执行其中的脚本与样式、重新渲染上千个 DOM 节点。这笔账怎么算都是亏的。
Ajax 给出的解法分两步。第一步,换运输方式:JavaScript 在后台向服务器发一个小请求,只要购物车的新数量,页面在此期间完全不受影响。第二步,换更新方式:拿到数字后,用 DOM 定位到购物车图标的角标元素,把它的文本从 3 改成 4,仅此而已。没有白屏,没有滚动丢失,用户甚至意识不到一次网络通信发生了。
这就是 Ajax 的全部直觉:把交互的最小单位从"整页"降到"一个元素",把传输的最小单位从"整份 HTML"降到"一段业务数据"。
下面的图把 Ajax 的四个组成部分和它们解决的问题放在一张视图里,后续章节会分别展开每一块。

Ajax 是 Asynchronous JavaScript and XML 的缩写——异步的 JavaScript 与 XML。这个定义来自 2005 年 Jesse James Garrett 的一篇文章,他为了描述 Google 系产品里那种"页面不刷新却能不断出新内容"的交互,把背后用到的几项技术打包命名。
但这个名字从诞生起就埋了一个误导:XML 只是当年的历史选择,今天早已名存实亡。早期的 Ajax 确实用 XML 承载数据,前端拿到响应后用 DOM 解析器一层层取节点,写法繁琐。JSON 后来居上的原因很实际:它更轻(同样的数据比 XML 小四到六成)、解析是浏览器内建的(JSON.parse 一行)、结构和 JavaScript 对象天然一致(拿到就能用,不需要"翻译")。今天说"Ajax 请求",默认传输的是 JSON;只有遗留系统和少数行业协议还在用 XML。名字沿用至今纯粹是历史惯性,就像"打字机键盘"的布局沿用到今天一样。
拆掉 XML 这个误区后,Ajax 的本质定义就干净了:
Ajax 是一套技术组合的用法范式:由 JavaScript 发起异步 HTTP 请求,在页面不重载的前提下获取服务器数据,并通过 DOM 操作只更新需要变化的部分。
注意"组合"两个字。Ajax 不是任何一项新技术——JavaScript、HTTP、DOM 全都是它出现之前就有的。它的新意在于用法:把这几项技术按特定方式组织起来,实现一种以前做不到的交互模式。这也是为什么面试里问"Ajax 是什么",好的回答从来不只说"XMLHttpRequest",而是描述这套协同。
第一个关键词"异步",针对的是传统请求的阻塞体验。
浏览器里负责执行 JavaScript 和负责渲染页面的是同一个主线程。传统同步请求(XHR 其实允许把第三个参数设为 false 走同步模式,现代规范已明确弃用)会让主线程原地等待服务器响应——响应回来之前,所有点击没反应、滚动不动、动画冻结,页面像死机。网络一慢,用户面对的就是一块僵住的屏幕。
异步模式换了个思路:JavaScript 调用发送方法后立即返回,请求的实际收发被交给浏览器内部的网络线程在后台执行。主线程腾出手来继续跑事件循环:响应用户的点击、更新动画、处理输入。等网络线程收到完整响应,再把"数据到了"这件事以事件回调(XHR)或 Promise 兑现(Fetch)的形式通知主线程,排队执行你写的处理函数。
一个直观的时间线对比:
同步模式: 点击 ── 发请求 ──[主线程死等 800ms]── 响应 ── 更新 ↑ 期间页面完全冻结 异步模式: 点击 ── 发请求 ── 立即返回 │(后台网络线程等 800ms) 期间用户可滚动、点按、输入 └── 响应到达 ── 回调排队执行 ── 更新
要强调一点:异步没有让请求本身变快,800ms 的网络耗时一毫秒都没少。它改变的是等待期间页面的状态——从"冻结"变成"照常运行"。体验上的差别远大于字面上的差别。
第二个关键词"局部更新",针对的是传输与渲染的双重浪费。
回到开头的购物车例子。传统模式下服务器必须重新输出整页 HTML,因为浏览器默认的更新单位就是"整个文档"。Ajax 把更新权交还给 JavaScript:服务器只返回一段业务数据(比如 {"count": 4},不到 20 字节),前端拿到后自己决定改哪里、怎么改。
DOM 层面的操作粒度可以精确到单个元素。角标是一个文本节点,把它的内容替换成 4,浏览器只需要重绘那一小块区域。对比整页模式下的重新解析 HTML、重建 DOM 树、重新计算样式与布局、重绘整个视口,开销差着几个数量级。
两种模式的量化对比:
| 维度 | 传统整页刷新 | Ajax 局部更新 |
|---|---|---|
| 传输内容 | 整份 HTML 加 CSS、JS 引用 | 仅业务数据(常见 JSON) |
| 传输体积 | 几十 KB 到几 MB | 几十字节到几 KB |
| 渲染范围 | 整页重绘 | 单个或少量元素 |
| 页面状态 | 闪白、滚动位置丢失 | 完全保持 |
| JS 与 CSS | 重新下载执行(无缓存时) | 不重复加载 |
| 服务器职责 | 渲染 HTML | 只出数据 |
体验改善只是表层,更深远的影响在架构层面。当"前端发请求拿数据"成为常规操作,后端就不必再为每个页面准备一套 HTML 模板,而是集中精力提供数据接口;前端则接管了全部渲染职责。这就是前后端分离的起点——两端通过 HTTP 上的 JSON 数据契约协作,各自独立开发、独立部署。
再往前走一步:如果一个应用的所有数据都通过 Ajax 获取、所有页面都由前端渲染,那浏览器只需要在首次加载时下载一个应用骨架,之后就是纯数据交互——单页应用(SPA)由此诞生。Vue、React 这些现代框架的基石之一,正是 Ajax 提供的这种"页面不动、数据流动"的能力。
一个常见问题是:既然现代开发都用框架的请求库,还值得从 Ajax 原始概念学起吗?值得。框架封装的是便利性,不变的是底层模型——你仍要面对状态码、跨域、错误分类、竞态问题,而这些问题的根源全在这一节描述的机制里。库帮你少写代码,不替你理解问题。
不是。JSON 只是当前的主流选择。接口返回 HTML 片段(前端直接插入)、纯文本、二进制数据(图片、文件)的场景都真实存在。"Ajax"约束的是"怎么传"(异步、不刷新),不约束"传什么"。
广义上算。业界日常语境里的"Ajax"已经从"用 XMLHttpRequest"泛化成"浏览器里异步取数更新页面"这一整套模式,用 Fetch 实现的请求同样在这个语义范围内。第 2 章会分别讲两种 API。
不一定。Ajax 只是给了你"只传数据、只改局部"的可能性,用得差照样慢:接口响应 2 秒、一个页面发几十个请求、每秒轮询打爆服务器——这些反面案例在 4.1 节性能优化里会集中讨论。工具降低下限的代价,不保证上限。
概念读十遍不如亲手做一遍。开一个本地静态页面(或直接在浏览器控制台配合任意返回 JSON 的测试接口),跑这个最小实验:在页面上放一个数字和一个按钮,点击后向接口请求一个随机数,只更新那个数字元素。实验时刻意做三个观察:
第一,点击按钮到数字变化之间,试着滚动页面、选中文本——一切照常,这就是"主线程未被阻塞"的体感。第二,打开网络面板看这次请求的体积,对比"查看网页源代码"看到的整页 HTML 体积——传输量的差距通常在一百倍以上。第三,把代码里的异步请求故意改错 URL,观察页面其余部分是否如常——失败的请求只影响它负责的那块区域,这是局部更新模式与传统整页模式的又一处差异(错误页会替换一切)。
做完这个实验再回头看本节的定义,"异步通信加局部更新"就不再是八个字,而是一组你已经亲眼验证过的行为。后续章节的所有内容——状态机、错误分类、竞态防护——都是在这个最小模型上,为真实世界的复杂性逐层加码。
还有一层值得现在建立的认识:Ajax 之于现代前端,地位类似于操作系统里的系统调用之于应用程序——绝大多数开发者日常通过封装库(axios、框架请求层)使用它,就像应用程序通过标准库使用系统调用。但正如理解系统调用才能诊断性能与权限问题,理解 Ajax 原生机制,才能诊断封装库之下的跨域、缓存、取消与竞态问题。本教程的组织顺序(原生机制在前、生态在后)正是为此:先把地基打牢,再去看地基上的建筑,每个楼层都看得懂承重墙在哪。