做前端这些年,AJAX 一直是面试里最容易被低估的一道题。日常项目里大家普遍用 axios 封一层就完事,可一旦后台反馈“参数收不到”、页面把 JSON 显示成乱码、GET 请求永远走旧缓存,能快速定位问题的人,往往不是框架 API 背得最熟的人,而是把原生 AJAX 原理吃透的人。这篇文章不整那些拗口概念,就按实战里最常见的调用场景,把请求发出、参数拼接、编码控制、响应解析,再到 jQuery 和 layui 里的 ajax 写法,一个个拆开配上能直接跑的实例。适合刚接触前后端联调的前端新手,也适合长期用封装库、想回头补一补底层细节的开发者。
1. 先理解 AJAX 解决的到底是什么问题
1.1 传统表单请求的体验痛点
在没有 AJAX 的年代,网页要做一次数据查询,通常就是整一个 form 表单,用户填完点“提交”,浏览器把整个页面参数发给服务器,服务器动态生成一个全新的 HTML 返回,浏览器整个刷新一遍。这个过程有几个非常糟的体验:刷新会白屏,用户在等待时什么都做不了;页面滚动条直接回到顶部,用户刚看了一半的内容位置也没了;表单里已填好的数据被清空,如果服务器校验不通过,用户得重新填一遍。
更麻烦的是,哪怕你只是想更新页面左下角一个列表,浏览器也会把整站脚本、样式、图片重新请求一遍。对后端来说,每次都要重新拼一整张页面,前端只能被动接收整包 HTML,根本没有“局部更新”这种概念。那时候做后台管理系统,页面上点一次查询,整个页面都会闪一下,操作频繁一点,用户眼睛都要花了。AJAX 出现以后,这种交互模式才被彻底改变:页面脚本自己发请求,拿到数据后只更新需要变化的那一小块 DOM。
1.2 异步请求是如何做到不阻塞的
AJAX 全称 Asynchronous JavaScript And XML,核心在 Asynchronous 上。它不是浏览器新发明的技术,而是把 XMLHttpRequest 对象暴露给了 JavaScript,让脚本有能力在页面不跳转的情况下发 HTTP 请求。传统请求是由浏览器导航发起的“整页往返”,AJAX 则是页面里的脚本注册一个异步任务,等网络线程和服务器通信完成后,再把回调丢进 JavaScript 的任务队列。
很多初学者不理解为什么“异步”这么重要。你可以类比成去餐厅吃饭:同步请求像是站在柜台前死等厨师做完菜,期间你在原地干瞪眼,什么也做不了;异步请求像是取个号回座位坐着,可以聊天、看手机,厨房做完喊号你再去端菜。浏览器主线程本来就是单线程的,如果发一个同步请求让主线程死等网络返回,页面上的点击、动画、滚动全部卡住,用户稍微多点两下浏览器甚至会提示“脚本无响应”。AJAX 的优势就在这里:请求发出去之后,send() 方法立刻返回,主线程继续渲染和处理交互,响应到了再执行回调。
1.3 一次请求的完整生命周期
要动手写 AJAX,首先要看懂 XMLHttpRequest 的 readyState。这个属性会经历五个阶段,每次变化都会触发 readystatechange 事件:
| readyState | 阶段名称 | 含义 |
|---|---|---|
| 0 | UNSENT | XHR 对象已经创建,但还没调用 open() |
| 1 | OPENED | open() 已调用,请求地址和方法已确定 |
| 2 | HEADERS_RECEIVED | send() 已调用,响应头已经接收到 |
| 3 | LOADING | 响应体正在下载中 |
| 4 | DONE | 请求完成,成功或失败都算完成 |
还有一个关键点:readyState 变成 4 不代表业务成功,它只代表“这次网络交互结束了”。最终状态要看 xhr.status,也就是 HTTP 状态码:200 到 299 之间一般算成功,404 是地址不存在,500 是服务端内部错误。所以判断响应能不能正常处理,必须同时看 readyState 和 status 两件事。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从零手写一个 AJAX 请求实例
2.1 先创建 XMLHttpRequest
原生 AJAX 的第一步是创建 XHR 对象,代码非常简单:
javascript复制var xhr = new XMLHttpRequest();
如果你在维护老项目,可能会看到这样一段兼容代码:
javascript复制var xhr = null;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
// IE6 及以下才需要,现在基本可忽略
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
ActiveXObject 那一段在新项目里完全可以不写。不过看老代码时要知道它是什么,不然会一脸懵。
2.2 open 和 send 方法
对象创建好之后,需要调用 open() 来初始化请求:
javascript复制xhr.open(method, url, async);
三个
