1.2 Ajax 的发展历史与演变


1.2 Ajax 的发展历史与演变

本节摘要:Ajax 不是一个发明,而是一段演化史的产物。从 1999 年微软为 Outlook Web Access 在 IE5 里塞进的 XMLHTTP ActiveX 控件,到 2005 年 Gmail 与 Google Maps 让世界看见无刷新交互的可能性,再到 2015 年 Fetch API 标准化——每一步演进背后都有一个具体的工程问题在推动。读懂这条时间线,你就明白今天的 API 为什么长成这样。

先看一段考古:1999 年,微软的邮件难题

故事的主角不是浏览器厂商的远见,而是一个具体的产品需求。1999 年前后,微软要做 Outlook 的网页版(Outlook Web Access),把桌面邮件客户端搬进浏览器。团队很快撞上一个体验墙:邮箱界面是典型的三栏布局——文件夹树、邮件列表、阅读窗格。按传统模式,用户点一封邮件,服务器要重新渲染整页,三栏全部重载,而用户其实只想让右下角那一块换内容。对一个每天要点几百封邮件的用户来说,这个体验不可接受。

于是微软的工程师在 IE5 里实现了一个 ActiveX 控件,叫 XMLHTTP,允许页面里的脚本直接向服务器发 HTTP 请求、拿回数据,全程不卸载当前页面。这就是 XMLHttpRequest 的直系祖先。注意它最初是 IE 专属的私有能力,通过 new ActiveXObject("Microsoft.XMLHTTP") 创建——这个写法你在老代码里可能还见过。

其他浏览器厂商随后跟进,把这个能力以 XMLHttpRequest 的名字原生实现(不再依赖 ActiveX)。到 2006 年,W3C 开始推进它的事实标准化;2008 年后各级浏览器补齐了跨域、进度事件、responseType 等能力。今天我们用的 XHR,是这条二十多年补丁链的最终形态。

Ajax 演化时间线

Ajax 二十五年演化时间线

Ajax 二十五年演化时间线

一、蛰伏期:能力早已存在,无人问津

一个反直觉的事实:从 2000 年到 2004 年,XMLHTTP 在 IE 里躺了四五年,业界几乎没有大规模使用。为什么?

原因在于能力不等于用法。当时主流的开发模式是"服务端渲染一切"——PHP、JSP、ASP 生成完整 HTML,前端只是展示层,没有独立的 JavaScript 工程体系。让页面脚本自己去发请求、管理数据与界面同步,既缺乏工具(没有成熟的调试器、没有框架),也缺乏需求(多数网站还停留在内容展示层面)。技术在等一个能证明它价值的场景,以及一批会用电它的人。

这个阶段给我们的启示是通用的:一项技术要真正流行,光有实现不够,还需要杀手级应用做示范、配套工具链降门槛、以及开发者心智的转移。三者齐备通常要十年。

二、爆发期:Gmail 与 Google Maps 的示范效应

2004 年 4 月 1 日 Google 发布 Gmail(当时还是邀请制),2005 年 2 月 Google Maps 上线。这两个产品让整个行业第一次直观看到"网页可以像桌面软件":Gmail 收发邮件不刷新页面、写邮件时界面即时响应;Maps 拖动地图时新图块无缝加载进来。当时的开发者打开开发者工具一看——咦,没有页面刷新,但网络请求一直在发。

2005 年 2 月,交互设计师 Jesse James Garrett 发表文章,把这些产品背后的技术组合命名为 Ajax。这个朗朗上口的名字起了巨大的传播作用:它把"XMLHTTP、DOM、JavaScript 协作"这种解释起来啰嗦的东西压缩成一个可谈论的名词,让产品经理、设计师、开发者终于有了共同语言。命名本身就是技术传播的关键一环。

随后是封装库的黄金期。2006 年 jQuery 发布,它的 ajax 方法把跨浏览器创建 XHR 的兼容代码(还记得 IE 的 ActiveX 写法吗)全部抹平,一行调用完成请求。Prototype、Dojo、Ext JS 同期提供了类似能力。封装库解决的是碎片化与繁琐:从此开发者不必关心"用户用的是 IE6 还是 Firefox",专心写业务逻辑。Ajax 从"高级技巧"变成了"基本功"。

这一时期还沉淀了一个至今有效的分工格局:原生 API 负责能力边界,封装库负责易用性。每当原生能力进化(如 Fetch 出现),封装库就向上移一层(从抹平兼容变成提供拦截器、取消、重试)。看懂这个分层,就看懂了整个前端请求生态的演化逻辑。

三、标准化时期:XHR Level 2 与能力补全

2011 年前后,"XMLHttpRequest Level 2"补上了几块关键短板,直接拓宽了 Ajax 的战场:

  • 跨域请求(配合 CORS 机制):以前跨域只能靠 JSONP 这种变通手段,现在有了正规协议。这是前后端分离部署成为可能的前提。
  • 进度事件upload.onprogress 让文件上传能显示进度条,下载也能拿到字节级进度。
  • 新的 responseTypeblobarraybuffer 让二进制数据(图片、音频、文件)可以走 Ajax 通道,不再需要插件。
  • FormData:表单数据(含文件)的构造与发送大幅简化。

这几个能力不是同时出现的,但它们共同把 Ajax 从"取 JSON 文本"扩展成"浏览器与服务器之间的通用数据通道"。今天你做的头像上传、图片预览、Excel 导出,底层都站在这批能力上。

与此同时,单页应用框架(Backbone、AngularJS,后来的 Vue 与 React)开始把"数据获取 + 状态管理 + 视图渲染"整合成完整方案。Ajax 从页面里零散的交互技巧,变成了应用架构的地基。

四、范式转移:Fetch API 与 Promise 化

XHR 用了十几年,暴露出几个设计层面的不满:API 基于事件回调,与日益函数化的 JavaScript 风格不合拍,错误处理散落在多个回调里;状态机复杂,readyState 的中间状态对多数场景无意义;组合性差,想要"请求 A 完成后取结果再发请求 B"的编排,回调嵌套很快就失控。

Fetch API(2015 年随 WHATWG 规范落地)是对这些不满的回答。它基于 Promise 设计:fetch 返回 Promise,then 链式消费,配合后来语法层面的 async/await,异步代码的形状几乎和同步代码一样。错误处理也统一进 Promise 的 rejection 通道。更妙的是 Request 与 Response 被抽象成独立对象,可以构造、克隆、传递——这为 Service Worker(拦截并缓存页面全部请求的机制)铺了路。

但 Fetch 也有被诟病的地方,而且很实际:它只拒绝网络错误,HTTP 404、500 不会触发 catch,必须手动检查 response.ok;早期版本没有原生的超时与取消(后来由 AbortController 补齐);进度监控反而不如 XHR 直接。所以"XHR 已死"的说法并不成立——上传进度等场景 XHR 仍是首选,第 4 章会展开。两个 API 的关系是并存互补,不是替代。

五、当下与去向:请求层的工程化

最近几年,变化的重点从"怎么发一个请求"转向"怎么管理一屋子请求":

  • Axios 成为最流行的请求库:拦截器统一处理鉴权头、错误上报,实例化配置 baseURL 与超时,取消令牌(后统一为 AbortController)管理请求生命周期。
  • React Query、SWR 这一代"请求状态管理库"把缓存、去重、后台刷新、竞态防护做成了默认行为,开发者只声明"这个组件需要哪些数据"。
  • Server-Sent Events、WebSocket 补上了"服务端主动推"的短板(详见 5.4 节),虽然严格说它们不属于 Ajax,但共同构成"浏览器与服务器通信"的完整工具箱。

回头看这条线:**每一代的答案都在变,但每一代的问题都是同一个——如何让浏览器里的界面与服务器上的数据保持同步,同时不打断用户。**掌握这条主线的人,学任何新请求库都快,因为新东西解决的永远是老问题的某个侧面。

常见疑问解答

为什么说 JSONP 是"变通手段"

早年 XHR 受同源策略限制不能跨域,开发者发现 <script> 标签加载脚本不受此限,于是让服务器返回"调用指定回调函数的脚本"来传数据。它能用,但只支持 GET、错误处理几乎为零、还有安全风险。CORS 出现后它就该退场了,今天基本只剩历史价值。

Fetch 会出现,是不是意味着该忘掉 XHR

不该。Fetch 是更好的默认选择,但 XHR 的上传进度事件、更细的兼容覆盖仍有不可替代的场景。工程判断的标准从来不是"新即对",而是"哪个能力匹配当前问题"。

本节要点回顾

  • 起点是 1999 年 IE5 的 XMLHTTP:为解决网页版邮箱的局部更新而生,最初是私有控件。
  • 2004-2005 是分水岭:Gmail 与 Google Maps 展示了无刷新交互的可能性,"Ajax"命名加速了传播。
  • 2006-2011 封装与标准化并行:jQuery 抹平浏览器差异,XHR Level 2 补齐跨域、进度、二进制能力。
  • 2015 Fetch 完成 Promise 化:更好的组合性与错误模型,但 HTTP 错误不自动 reject、进度支持弱于 XHR,两者是互补而非替代。
  • 当前焦点是请求层的工程化:缓存、竞态、拦截器——管理"一屋子请求"比"发一个请求"更难。
  • 主线问题从未变过:让界面与服务器数据保持同步,同时不打断用户。

历史给当下的三个启示

把二十五年时间线读完,值得沉淀的不只是年份与事件,还有几条能迁移到任何技术评估上的判断方法。

**启示一:找"被问题塑形"的痕迹,判断技术的生命力。**XMLHTTP 为邮箱而生、Level 2 的跨域能力为前后端分离铺路、Fetch 的 Promise 化为 Service Worker 铺路——每一项能力出现时,背后都站着一个明确的、当时无解的问题。评估一项新技术时同样可以问:它到底为什么问题而生?如果答案含糊("更现代""更灵活"这类词),多半是方案在找问题,而不是问题在等方案;如果答案具体("解决了取消与超时的统一机制"),值得深入。用这条标准回看自己技术选型的历史,命中率会明显提高。

启示二:命名与传播是技术流行的一半。"Ajax"这个朗朗上口的名字对技术普及的推动,不亚于任何一项工程改进;同期更早出现的类似能力(微软的远程脚本技术)因为没有一个好名字与一篇好文章,被埋没了五年。这不是玄学:一个好名字压缩了解释成本,让不同角色(产品、设计、开发)能共享同一个概念。今天评估内部平台的通用能力时,"给它起个准确又好记的名字"值得当成正经的工程任务,而不是修辞小事。

**启示三:分层不塌,生态不乱。**原生层提供能力边界、封装层提供易用性、框架层提供架构整合——Ajax 生态四分之一个世纪没有出现"一层吞掉另一层"的坍塌,Fetch 出现后 jQuery 退位但 Axios 上位,Axios 普及后 React Query 又在其上生长。理解这个分层规律,就理解了"学底层"与"用上层"并不矛盾:上层每换一茬,底层知识都在为新上层服务。反过来说,只学过某一层封装的人,每次生态换血都要从零再学。

最后留一个思考题作为本节的收束:如果把时间拨回 2004 年,你是那个第一次打开 Google Maps 的开发者,能不能从"拖动地图不刷新"这个现象出发,反推出背后需要哪些能力(后台请求、数据分块、局部渲染、缓存预取)?做这个思想实验时你会发现,多数能力你都能推出来——因为前两节已经把约束条件讲清了。从现象反推机制,再从机制预见下一代方案,这是历史学习给技术者的终极回报:它把你从"技术的使用者"变成"技术的评估者"。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U