本节摘要:传统 Web 请求以"文档"为单位——任何交互都导致浏览器卸载当前文档、请求新文档、重新渲染整页;Ajax 以"数据"为单位——浏览器保留当前文档,JavaScript 后台取回数据并局部修改。本节用一次"点赞"操作在两种模式下的完整旅程做逐帧对照,量化传输、渲染、状态保持的差异,并说明这场变革如何重塑了前后端分工。
同一个功能,两套代码,用户的感受天差地别。
页面是一个图文详情页:顶部一张大图,中间三千字的文章,底部一排点赞、收藏、分享按钮,用户已经读到了文章中部。此刻他点了"赞"。
传统模式的旅程:点赞按钮是一个链接或表单提交,浏览器发起导航请求。当前页面被卸载——此刻意味着文章里正在播放的什么都会中断、滚动位置被丢弃。服务器把"点赞数加一"写入数据库,然后重新渲染整张详情页的 HTML 返回。浏览器解析新文档、加载资源(有缓存则快些)、重建 DOM、重排版重绘。用户最终看到点赞图标亮了,但视口弹回了页面顶部,刚才读到的那一段得重新滚回去找。他心里骂了一句,读书的心气断了。
Ajax 模式的旅程:点击按钮,JavaScript 拦截默认行为,向服务器发一个小请求。服务器只做一件事——点赞数加一,返回 {"likes": 1025} 这样十几个字节。页面从头到尾没有被卸载,滚动位置原封不动,响应回来后脚本把按钮旁边那个数字元素的文本换成 1025、给按钮加个高亮样式,结束。整个过程用户甚至没有感知到网络往返。
差别不在"快慢",在交互的单位。传统模式里,浏览器眼中每次交互都是"要一份新文档";Ajax 模式里,交互只是"交换一份数据"。这个单位的降级,带来后面所有量化的差异。

对比不能停在"体验更好"这种形容词上,要拆成可度量的维度。
**传输维度。**假设详情页 HTML 120 KB(含内联结构与脚本引用),一次传统点赞要把这 120 KB 全部重传(哪怕有缓存,协商请求至少也要一个往返);Ajax 模式传输的是约 20 字节的 JSON,加 HTTP 头也就几百字节,压缩后更小。对高频交互(点赞、收藏、轮询状态)累积起来,是数十倍到数百倍的流量差。移动端弱网环境下,这个差距直接体现在用户等待时长上。
**渲染维度。**整页刷新要重走完整渲染管线:解析 HTML 构建 DOM、解析 CSS 计算 style、生成布局树、绘制合成,中间还有脚本执行可能反复触发重排。局部更新只改一个文本节点,浏览器只需对受影响的小区域重绘。两者的 CPU 开销不在一个数量级——这也是低端手机上整页刷新明显卡顿、局部更新依然顺滑的原因。
**状态维度。**这是最容易被忽略、却最伤害体验的一项。页面卸载会带走一切运行时状态:滚动位置、未提交的表单草稿、展开的折叠面板、播放中的媒体、正在进行的选择操作。传统模式下"保住状态"需要服务端把状态编码进新页面的 URL 或表单里,成本高且极易遗漏;Ajax 模式中文档根本没动,状态天然保全。
**服务器维度。**传统模式下每次交互都要服务端执行完整模板渲染;Ajax 模式下服务端只序列化一份数据,没有模板开销。更重要的是职责变化:服务器从"页面工厂"变成"数据服务"——同一个接口可以喂网页、喂手机端、喂第三方调用者,复用价值完全不同。
| 维度 | 传统整页请求 | Ajax 请求 |
|---|---|---|
| 交互单位 | 文档 | 数据 |
| 典型传输量 | 整页 HTML(几十 KB 起) | JSON(字节级到 KB 级) |
| 页面状态 | 卸载重建,全部丢失 | 保留,滚动输入均在 |
| 渲染成本 | 完整渲染管线 | 单区域重绘 |
| 服务器职责 | 渲染 HTML 页面 | 输出数据接口 |
| 可缓存性 | 页面级 | 接口级,粒度更细 |
| 失败影响 | 错误页替换整个页面 | 可局部提示并恢复 |
| 接口复用 | 几乎不可复用 | 网页、移动端、第三方共用 |
用最小可运行的例子感受两种模式。传统模式下点赞是一个链接(也可以是表单),浏览器导航:
<!-- 传统:一次点击等于一次页面跳转 --> <a href="/article/42/like">点赞 (1024)</a>
服务器端渲染框架接到这个请求,写入数据库,然后重定向或直接重新渲染详情页模板。前端代码只有一行,代价是交互单位是整页。
Ajax 模式下,按钮承担一次完整的小型数据流:
// Ajax:拦截默认行为 换成一份数据交换 const btn = document.querySelector('#like-btn'); const count = document.querySelector('#like-count'); btn.addEventListener('click', async () => { btn.disabled = true; // 防重复点击 try { const res = await fetch('/api/articles/42/like', { method: 'POST' }); if (!res.ok) throw new Error('HTTP ' + res.status); const data = await res.json(); // { likes: 1025 } count.textContent = data.likes; // 只改这一个数字 btn.classList.add('liked'); // 只改这一个样式 } catch (err) { showToast('点赞失败,请稍后再试'); // 失败原地恢复 不炸页面 } finally { btn.disabled = false; } });
代码量多了几倍,换来的是:页面不动、状态保全、失败可原地恢复。注意这段代码里已经埋了第 3 章要展开的主题——防重复提交、错误分类、界面状态恢复。传统模式下这些要么做不了,要么要用整页跳转这种重伤才能实现。
还要注意两段代码里服务器的角色差异:前者需要一个"渲染详情页"的完整模板流程;后者只需要一个纯数据接口,返回值连 HTML 都不含。同一个业务动作,服务器端的工作形状完全不同。
技术差异积累到一定程度会改变架构。Ajax 普及之后,前后端的分工线从"页面的哪里"移到了"数据的两头":
传统架构(服务端渲染):后端掌管路由、业务、模板渲染,前端工程师在模板里填标签,浏览器只是显示终端。优点是首屏快、对客户端要求低;缺点是每次交互都动用整页渲染,前后端代码耦合在同一模板里,无法独立演进。
分离架构(接口加前端渲染):后端只提供 JSON 接口与业务逻辑,前端接管路由与全部渲染。交互不再依赖整页跳转,一个应用可以只加载一次骨架。代价也真实存在:首屏需要先取数据再渲染(有白屏窗口,需要加载态设计)、接口需要设计版本与文档、跨域与鉴权成为必答题。
现实工程里两者经常混用:内容展示页(文章、商品详情)用服务端渲染保证首屏与检索友好,交互密集区(评论、点赞、购物车)用 Ajax。判断标准不是流行度,而是这个区域的交互频率与状态保持需求——交互越频繁、状态越要保,越该走 Ajax。
💡 一个判断口诀:改"一小块、很频繁"的交互用 Ajax;"整页换内容、低频"的跳转用导航。点赞用 Ajax,翻到另一篇文章用导航,中间地带(比如列表分页)看状态保持需求。
讲差异不能只报喜。Ajax 模式把复杂度从前端挪走了一部分,又搬进来另一部分:
这些代价不是否定 Ajax,而是提醒:选它是因为交互单位匹配,不是因为它是先进名词。把低频的整页内容切换硬做成 Ajax,只会白付复杂度。
取决于有没有被拦截。原生表单提交(不阻止默认行为)就是传统请求,会导航;用 JavaScript 接管 submit 事件、改成 fetch 发送,就是 Ajax。同一个表单两种命运,3.2 节专门做这个改造。
不会。Ajax 不产生导航,不写入历史栈。这也是上一条"书签问题"的根源。需要让某种状态可前进后退时,要配合前端路由主动往历史栈里压记录。
不一定。内容型网站(新闻、文档)用户要的是"最快看到正文",服务端渲染往往更合适;工具型、交互型应用(编辑器、后台、社交流)才真正受益于 Ajax 与 SPA。架构选择的第一性问题永远是用户的使用方式。
把本节的判断标准浓缩成一张可以贴在工位上的决策卡。面对"这个交互该不该用 Ajax"时,依次回答三个问题:改动范围小于页面三分之一吗?操作频率高吗(一天多次而非一次)?页面状态(滚动、输入、展开态)需要保持吗?三个"是",坚定用 Ajax;三个"否",坚定用导航;中间地带按权重取舍——状态保持的需求权重最高,因为它是传统模式最难补、Ajax 模式免费送的一项。
再补一个团队协作视角:模式选择也是沟通成本的选择。整页刷新模式下,前后端讨论的是"页面长什么样";Ajax 模式下,讨论的是"接口契约长什么样"(2.4 节的主题)。契约讨论前期更费劲(要定义结构、错误码、边界情况),但一旦定型,两端可以并行开发、独立演进——这笔前期投入换来的是后期变更成本的大幅下降。反之,用整页模式躲开契约讨论的团队,往往在项目中期被"改一个字段要动两端、发两次版"的摩擦力拖住。选择交互模式,某种意义上也是在选择团队的协作结构。