6.1 AJAX 基础:从开腹到腔镜 在 AJAX 出现之前,页面要拿一份新数据只有一条路:整页刷新重开——相当于每次复查都重新开一次腹。AJAX 的名字里那些字母(异步的 JavaScript 与 XML)如今一半过时了,核心思想却一直没变:让脚本在不刷新页面的前提下,自己去和服务端交换数据。本章从原理讲起,本节先把一次异步请求的完整生命周期、同源规则的安检门、数据的进出格式这三件事立起来——后面的器械解剖全站在它们之上。 本节摘要:AJAX 的本质是浏览器提供的异步请求通道:请求发出后脚本继续执行,响应到达时回调被唤醒;通道受同源策略约束——协议、域名、端口任一不同即视为跨域;数据以查询串或请求体出去,以 JSON 为主流格式回来。
在 AJAX 出现之前,页面要拿一份新数据只有一条路:整页刷新重开——相当于每次复查都重新开一次腹。AJAX 的名字里那些字母(异步的 JavaScript 与 XML)如今一半过时了,核心思想却一直没变:让脚本在不刷新页面的前提下,自己去和服务端交换数据。本章从原理讲起,本节先把一次异步请求的完整生命周期、同源规则的安检门、数据的进出格式这三件事立起来——后面的器械解剖全站在它们之上。
本节摘要:AJAX 的本质是浏览器提供的异步请求通道:请求发出后脚本继续执行,响应到达时回调被唤醒;通道受同源策略约束——协议、域名、端口任一不同即视为跨域;数据以查询串或请求体出去,以 JSON 为主流格式回来。
先看一段代码的执行顺序,把"异步"落成看得见的事实:
console.log('1:准备发请求'); $.ajax({ url: 'api/queue', success: function (data) { console.log('3:响应到达,条数', data.length); }}); console.log('2:请求已寄出,我继续干活');
控制台的输出顺序必然是 1、2、3。$.ajax 把请求托付给浏览器后立刻返回,脚本毫不等待;等响应在将来的某个时刻到达,回调才被唤醒执行。这就是"异步"的全部含义——发出与到达之间,代码的世界照常运转。它带来的两个工程后果贯穿本章:其一,回调里的变量状态可能与发出时不同(期间用户可能点了别的);其二,请求可能永远不回来(网络断了),所以错误处理不是可选项。

脚本默认只能向"自己家"发请求。同源的判定看三样东西:协议、域名、端口——三者全部一致才是同源,任何一项不同都是跨域,请求会被浏览器拦下。http 对 https、不同端口、连子域名不同都算。这道安检门的存在解释了本章的两个设计:为什么 6.4 节要专门开凿跨域侧路,以及为什么开发期前端常用"代理转发"把外部接口转成同源地址。
同源策略拦的是读取响应,不是发出请求本身——这个细节决定了 JSONP 这类老技术的可行性:请求其实发出去了,只是脚本读不到回包。侧路怎么凿,6.4 节展开。
出去的数据通常是对象,jQuery 按请求方式自动打包:GET 请求把对象拼成查询串(name=张三&bed=b),POST 请求放进请求体。回来的数据按 dataType 声明反序列化,json 是当代绝对主流:
$.ajax({ url: 'api/queue', type: 'GET', data: { ward: 'a', onlyWaiting: true }, // 对象自动拼成查询串 dataType: 'json', // 响应按 JSON 解析成对象 success: function (data) { console.log(data.length, data[0].name); // 到手就是 JS 对象数组 } });
dataType 没写时 jQuery 会嗅探响应头猜类型,多数时候猜得对;但显式声明能让"服务端头给错"这类坑提前暴露。历史上 AJAX 名字里的 XML 已彻底让位给 JSON——更轻、与 JS 对象天然同构。HTML 片段是另一常见形态,配合 6.3 节的 load 有专门的快捷通道。
jQuery 的 AJAX 底层是浏览器提供的 XMLHttpRequest(XHR)对象——名字古怪,因为它诞生时的雄心是搬运 XML。XHR 的事件模型繁琐,jQuery 的价值一大半在把它封装成人话。现代浏览器给的继任者是 fetch:
fetch('api/queue?ward=a') .then(function (res) { return res.json(); }) // 第一步:解析响应 .then(function (data) { console.log(data.length); }) .catch(function (err) { console.error('请求失败', err); });
两代接口的性格差异值得现在记住:fetch 用 Promise 链、两段式取数(先拿响应头再解析正文),状态码不为成功时它不会自动走失败分支,需要自己检查 res.ok;jQuery 则把非 2xx 状态直接归入 error 回调。这个差异是"从 jQuery 迁到 fetch 后错误处理全失效"的标准成因,6.2 与 6.4 节还会两次回到它。
把一张请求的旅程拆成可指认的站点,排错时才能说清"坏在哪一站":
| 站点 | 此刻发生什么 | 坏在这里的典型症状 |
|---|---|---|
| 发起 | 脚本调用接口、带上配置 | 报函数未定义——库没引入或版本 slim |
| 序列化 | 对象打包成查询串或请求体 | 服务端收到空参数——字段名拼写不齐 |
| 在途 | 浏览器与服务器往返 | 长时间无响应——地址错、网络断、缺超时 |
| 安检 | 同源策略核对三件套 | 控制台报跨域阻止——协议域名端口有一样对不上 |
| 反序列化 | 按 dataType 解析回包 | success 里拿到字符串——声明缺失或服务端头错 |
| 分流渲染 | 回调把数据缝进页面 | 请求成功页面空白——选择器没命中或结构差一层 |
这张表的价值在"归位":看到症状先对号入座,再进对应小节的机制复述——它和第 7 章的排查图谱是同一套思路在数据链路上的应用。
背景:标本页要显示候诊人数。第一次实现的代码只有 success 分支,联调时后端偶发返回服务错误,页面的人数栏永远停在"加载中"。
操作分三步修复。第一步补失败分支:error 回调里把人数栏改成"暂时取不到数"并保留旧值;第二步补超时:timeout: 5000 让"永远在途"的请求有明确的死法;第三步补兜底:complete 回调里关掉加载指示,无论成败都不让转圈赖着不走。
$.ajax({ url: 'api/queue/count', dataType: 'json', timeout: 5000, success: function (data) { $('#count').text(data.waiting); }, error: function () { $('#count').text('暂时取不到数'); }, complete: function () { $('#loading').hide(); } });
结果:断网复测,人数栏给出明确文案、转圈准时消失。解读:数据链路的完整版必须三路齐全——成功路渲染、失败路降级、收尾路复位;只写成功路的链路在真实网络下等于半个功能。变式:若取数发生在按钮点击后,还应在发起前禁用按钮、complete 里恢复,顺带把重复提交也防了——这个手法在 6.2 的参数表里叫 beforeSend,此处先见到它的用法。
dataType 反序列化;JSON 是当代主流,显式声明更稳。通道原理立住了。下一节解剖主力器械:$.ajax 的全参数、三类回调、全局事件与 Promise 三种消费方式。