资讯动态

从手写AJAX到fetch/axios:XMLHttpRequest底层原理完全解析

发布时间:2026/10/17 14:57:17 来源:尧图企业网站定制
1. 为什么不直接推荐 fetch先把 AJAX 的前世今生讲明白很多新人第一次接触前端异步请求时看到的已经是fetch甚至axios的教程很少有人真正从头讲清楚 AJAX 是什么、解决的是什么问题。我记得自己刚入行那会儿对着XMLHttpRequest这个名字一头雾水——既不懂为什么叫“XML”也不懂为什么要“异步”。直到某次在某个公司实习被安排给一个老项目加功能那个项目里所有的接口调用全是XMLHttpRequest手写的我才被迫把这块知识补完整也是从那时候开始我才意识到不理解 AJAX后面学 fetch 也好、学 axios 也好都只是在套模板一旦出问题就完全不知道从哪排查。AJAX 的全称是 Asynchronous JavaScript And XML翻译过来就是“异步 JavaScript 和 XML”。它最早是微软在 1999 年左右引入的概念随后被各大浏览器厂商跟进逐渐成为 Web 开发的事实标准。它解决的核心问题非常简单在不刷新整个页面的前提下让 JavaScript 能够向服务器发起请求、接收响应、然后更新页面局部内容。你可以想象一下没有 AJAX 的网页是什么样子每次点赞、每次搜索、每次提交表单都要整页刷新用户体验极其割裂。而现在我们能享受到单页应用、局部刷新、实时提示这些体验底层都离不开 AJAX 这套机制。说句实在话即便是 AI 和框架横行的现在AJAX 也远没有过时。fetch本质上是基于 Promise 的新式 APIaxios底层在浏览器环境中也仍然用的是 XMLHttpRequest只是封装得更友好。所以学 AJAX 并不是学一门“过时的技术”而是把异步请求的地基打牢。这篇文章不会一上来就讲fetch而是老老实实从XMLHttpRequest入手把它拆开揉碎让你理解请求从发出到接收响应的完整链路。文章会覆盖这样几个部分AJAX 的核心运行机制、用原生 XMLHttpRequest 完整手写 GET/POST 请求、响应状态与数据解析、最容易踩的坑异步时序、中文编码、跨域问题以及最后我会给出一个完整的小案例帮你把零散的知识串起来。适合完全没有接触过 AJAX 的初学者也适合那些一直用 axios 但没搞懂底层逻辑的开发者。2. 从一次页面局部刷新说起AJAX 的核心运行机制你可能会好奇为什么 AJAX 能做到不刷新页面却能拿到新数据这里面的关键其实不在 JavaScript 本身而在于浏览器给我们提供了一套“额外”的能力这套能力允许 JavaScript 运行环境也就是浏览器里的 JS 引擎在背后偷偷发起一个网络请求并且监听这个请求的进展状态。2.1 你与服务器之间的“使者”XMLHttpRequest 对象要发起 AJAX 请求第一步永远是创建XMLHttpRequest对象。它就像是你的 JavaScript 代码与服务器之间的一位“使者”负责把你的请求带出去再把服务器的响应带回来。它的名字虽然带着 XML 三个字母但现在的实际使用中它传递的数据绝大多数是 JSON 或纯文本XML 反而很少见这其实只是个历史遗留的命名而已。创建这个对象只有一行代码var xhr new XMLHttpRequest();所有浏览器都支持这样创建包括老旧的 IE 系列当然 IE 有自己远古版本的 ActiveX 写法现在我们可以完全忽略它。创建好对象之后这并不是说请求就已经发出去了它只是给你准备了一个空壳子你可以在这个壳子上配置请求方式、请求地址、是否异步、请求头、请求体等信息。很多初学的人不理解“对象”在这里面究竟扮演什么角色我打个比方你去营业厅寄快递new XMLHttpRequest()相当于是你领到了一张快递单然后你在快递单上填寄件人、收件人、物品信息这个过程就是设置请求参数最后你把快递单交给柜员柜员帮你把快递发出这就是send()方法。整个过程中你还可以通过运单号去查询快递走到哪了、有没有派送成功这对应的是浏览器给我们的各种状态信息和事件回调。2.2 xhr.open()配置你的请求而不是发送它创建好XMLHttpRequest对象之后接下来要用open()方法来配置请求。请注意很多人第一次看到open()就以为这是在“打开连接”实际它只是准备请求、设置参数。open()接收三个主要参数xhr.open(method, url, async);第一个参数是 HTTP 方法常用的有GET、POST、PUT、DELETE不同方法代表不同的语义比如 GET 一般是获取数据POST 一般是提交数据第二个参数是请求地址第三个参数是是否异步这个参数极其关键——如果你传false浏览器会在这里卡住一直等到服务器返回响应之后才继续往下走页面因此会被锁死用户界面失去响应体验极差。默认情况下它是true即异步执行这也是推荐的做法。为什么推荐“异步”因为如果同步发送请求浏览器的主线程会一直等待服务器返回数据期间用户点击页面上的任何按钮都没有反应就像你看视频的时候画面卡住了一样CTRL 什么都操作不了。而异步模式则允许我们先把请求发出去然后继续做页面上其他事情服务器响应返回后再通过回调函数来处理数据。这也是 AJAX 里那个字母 AAsynchronous存在的意义。open()之后请求还没有发出去你可以继续设置请求头信息比如Content-Type、超时时间、是否携带 Cookie 等等。这些都设置好之后调用send()才算真正把请求发出去。2.3 readyState 与 status两个不一样的状态你别搞混在使用 XHR 的过程中有两个状态很多人会混淆一个是readyState一个是status。readyState表示这个请求对象的生命周期状态它有五个值值状态含义0UNSENT已创建 XHR 对象但还没调用 open()1OPENED已调用 open()但还没调用 send()2HEADERS_RECEIVEDsend() 已调用浏览器已经收到服务器响应头3LOADING正在下载响应体内容可能还没下载完整4DONE响应内容已经完全接收请求流程结束而status表示的是 HTTP 响应状态码也就是我们平时说的 200、404、500 这些。它表达的是服务器对这次请求的处理结果200 表示成功404 表示请求地址不存在500 表示服务器内部错误。我来梳理一下它们之间的关系一次完整的请求过程中readyState会 0 → 1 → 2 → 3 → 4 变化而status只有收到响应头之后才真正有意义。我们通常判断请求是否成功会看readyState 4请求流程走完且status 200服务器处理成功。两个条件同时满足才算是拿到了有效响应。这里有一个很容易被误解的细节readyState 4并不代表一定成功。如果服务器返回 404 或 500readyState照样会走完整个流程变成 4只是status不是 200 而已。所以判断成功的唯一标准是 status 是否落在你期望的范围内大多时候是 200 到 299 之间。很多新手判断“是否成功”只看 readyState导致 404 的页面被当成成功处理拿到的还是错误的响应内容这就是没搞清楚两个状态导致的。2.4 事件监听readystatechange 是你观察请求进程的窗口光有readyState还不行你得有一种方式感知到它变化了。XHR 对象提供了一个事件readystatechange。当readyState的值发生变化时这个事件就会被触发。我们通过给xhr.onreadystatechange赋值一个函数就能在这个事件到来时检查状态、读取数据。xhr.onreadystatechange function() { if (xhr.readyState 4 xhr.status 200) { console.log(xhr.responseText); } };这段代码使用responseText属性读取服务器返回的文本内容。这个属性的值只有在readyState 4之后才有完整意义因为之前可能只接收到了部分内容。所以把读取逻辑放在readyState 4的判断里是绝对安全的做法。除了readystatechange现代浏览器还支持load事件它在请求成功完成时触发比readystatechange更简洁。但作为入门我推荐你先搞懂readystatechange因为它在各种老项目里出现频率极高而且它能让你看到整个请求过程的全部状态变化对理解机制帮助很大。3. 手写一个完整的 GET 请求从 URL 拼接到响应解析理解了对象和状态之后我们来看一个完整可运行的 GET 请求案例。GET 请求的特点是通过 URL 传递参数服务器根据这些参数返回相应的数据。最常见的场景就是搜索、分页、筛选这些操作。3.1 拼接 URL 时的参数转义encodeURIComponent 不能省假设我们要请求一个身份验证的接口需要传递两个参数用户名name和年龄age。你可能会直接写出这样的地址var url https://example.com/api/user?name张三age18;这个写法在中文参数不多时可能没问题但如果参数里含有特殊字符如、、?、空格、#就会破坏 URL 结构导致服务器解析出错。比如 name 的值如果本身就叫张三李四那服务器会认为name是张三李四是另一个参数的开始整个请求参数就乱了。所以正确做法是用encodeURIComponent()对每个参数值进行编码var name 张三 三; var age 18; var url https://example.com/api/user?name encodeURIComponent(name) age age;encodeURIComponent会把中文、空格、特殊符号转成浏览器和服务器都能正常识别的百分号编码格式。这个细节很多人一开始完全不在意直到某天线上出现乱码或者请求直接 400 报错才开始排查到这一层。3.2 发送 GET 请求的标准姿势写好了 URL接下来就是定义XMLHttpRequest对象配置并发送。这里演示一个完整的 GET 请求代码var xhr new XMLHttpRequest(); xhr.open(GET, url, true); xhr.onreadystatechange function() { if (xhr.readyState 4) { if (xhr.status 200 xhr.status 300) { var result JSON.parse(xhr.responseText); console.log(请求成功拿到的数据是, result); } else { console.error(请求失败状态码, xhr.status); } } }; xhr.send();send()在这里不传任何参数因为 GET 请求的参数都拼在 URL 里了不需要在请求体中再传数据。这是一个极其常见但新手也极容易困惑的问题为什么 GET 请求要调send()其实send()的作用是“发出请求”它和是否传参数没有必然关系再强调一遍GET 也要调send()只是参数为空。3.3 响应文本与响应JSONresponseText 与 JSON.parse当服务器返回的数据是 JSON 格式时xhr.responseText拿到的是一段 JSON 字符串比如{name:张三,age:18}。字符串本身没法直接访问name属性所以需要先用JSON.parse()把它转换成 JavaScript 对象。有些同学会问为什么不能用xhr.responseJSON之类的方式直接拿到对象早期的浏览器确实没有统一的 JSON 响应解析机制发展到后来XHR虽然也支持responseType json但在老项目和跨浏览器层面最稳妥、兼容性最好的做法仍然是先拿到文本再手动JSON.parse()。需要注意的是如果服务器返回的不是合法 JSON比如返回了一段 HTML 错误页面JSON.parse会直接抛出异常导致后面代码无法执行。所以生产环境里解析前对responseText做一下格式判断或者用 try-catch 包一层是非常有必要的var result null; try { result JSON.parse(xhr.responseText); } catch (e) { console.error(返回的数据不是合法JSON原始内容, xhr.responseText); }这个小小的防御性写法能在线上环境帮你省去大量排查时间。4. 再写一个完整的 POST 请求请求头、请求体和表单提交POST 请求和 GET 最大的区别在于参数通常放在请求体request body里而且需要明确告诉服务器请求体的格式。AJAX 中最常见的 POST 提交格式有application/x-www-form-urlencoded表单格式和application/jsonJSON 格式。这两种格式的发送方式略有不同下面分别说。4.1 表单格式Content-Type 与数据序列化当你以表单格式提交数据时请求体验证串的格式和 URL 查询参数类似也是name张三age18这种键值对。但注意光把数据拼成这个格式还不够你需要在请求头里告诉服务器“我发送的是表单格式”对应的头就是xhr.setRequestHeader(Content-Type, application/x-www-form-urlencoded);为什么要设置这个头因为服务器需要知道如何解析你发过来的请求体。如果服务器不明确你发的数据格式它可能只把请求体当作一段普通文本无法提取出参数。setRequestHeader必须在open()之后、send()之前调用顺序错了浏览器会直接报错。完整的 POST 表单提交代码如下var xhr new XMLHttpRequest(); xhr.open(POST, https://example.com/api/login, true); xhr.setRequestHeader(Content-Type, application/x-www-form-urlencoded); var data username encodeURIComponent(张三) password encodeURIComponent(abc123); xhr.onreadystatechange function() { if (xhr.readyState 4) { if (xhr.status 200 xhr.status 300) { console.log(xhr.responseText); } } }; xhr.send(data);注意send()里传入了计算好的字符串这就是请求体。encodeURIComponent同样不能省因为密码、备注这些字段里很可能含有特殊字符。4.2 JSON 格式JSON.stringify 与对应的 Content-Type现在很多后端接口更偏爱 JSON 格式因为结构清晰、支持嵌套对象。发送 JSON 数据时请求头要改成xhr.setRequestHeader(Content-Type, application/json;charsetUTF-8);而请求体不再是键值对字符串而是 JSON 字符串化之后的结果var dataObj { username: 张三, password: abc123, tags: [admin, user] }; var data JSON.stringify(dataObj); xhr.send(data);这里用JSON.stringify把一个 JavaScript 对象转成 JSON 字符串。注意send()接收的参数类型可以是字符串、可以是FormData但 JSON 对象不能直接传进去必须先 stringify。4.3 表单数据序列化的一个小技巧serialize 思路如果表单字段很多一个个手动拼接字符串非常费劲。一个常见的做法是遍历表单元素自动序列化。原生实现也不算难比如我们可以把表单里所有带name属性的字段取出来拼接成查询字符串。稍微封装一下就能得到类似 jQuery 的.serialize()效果。但这个封装涉及遍历 DOM代码量不短所以很多老项目干脆引入一个轻量工具库处理。如果你想省事又不想引库我的建议是先收集数据为一个对象再用一个循环去拼接var params { username: 张三, email: zhangsanexample.com, city: 上海 }; var parts []; for (var key in params) { parts.push(encodeURIComponent(key) encodeURIComponent(params[key])); } var formDataString parts.join();这段代码其实就是表单序列化的核心逻辑。理解它之后再去看各类前端框架里的序列化方法就会发现都是同一个套路。5. 最容易踩的五个深坑时序、乱码、超时、跨域与兼容学 AJAX 最头疼的不是写不出请求而是出现问题找不到原因。根据我这些年带人和自己踩坑的经验下面这五个问题是最常见的几乎每个初学或者刚接触原生 XHR 的开发者都会遇到。5.1 异步时序陷阱为什么拿不到 return 值这是新人问得最多的一个坑。很多人想写一个函数用 AJAX 获取数据然后直接return给调用方。比如function getData() { var responseData null; var xhr new XMLHttpRequest(); xhr.open(GET, https://example.com/api/data, true); xhr.onreadystatechange function() { if (xhr.readyState 4 xhr.status 200) { responseData xhr.responseText; } }; xhr.send(); return responseData; // 这里返回的永远是 null }为什么永远是null因为函数执行到return的时候异步请求根本还没返回responseData还没来得及被赋值。异步执行流程就好比你在餐厅点菜——你点完菜发出请求之后不可能马上尝到菜要先处理别的等菜端上桌了再吃。这和我们平时写代码“从上到下执行”的思维惯性截然不同。解决这个问题有几种思路一种是把后续逻辑放进回调函数里处理一种是用 Promise 包裹 XHR返回一个 Promise 对象让调用方用.then()拿结果。我强烈建议入门阶段先习惯回调写法理解异步的本质再去上 Promise 封装。直接上 Promise 很容易让你跳过底层逻辑出了问题依旧一头雾水。5.2 中文乱码请求和响应两端都要管中文乱码这个坑我印象很深。有一次帮同事排查问题发现接口返回的中文全是“锟斤拷”这类乱码折腾了很久才发现是服务器返回的字符编码是GBK而页面默认按UTF-8解析。作为前端我们能做的主要是两件事第一请求时在Content-Type里显式地带上;charsetUTF-8让服务器知道我们发送的数据编码第二处理响应时如果服务器返回的是UTF-8那浏览器默认能正确解析但如果是其他编码可能需要在拿到responseText后做转换或者干脆要求后端统一返回UTF-8。在实际项目中前后端统一用 UTF-8是最省心的方案。如果你在公司里遇到老接口乱码第一优先级是找后端对齐编码规则而不是前端硬解。5.3 超时与网络错误onerror 和 ontimeout 别忽视一个健壮的前端请求必须考虑请求失败的情况。XHR 提供了timeout属性用来设置请求超时时间毫秒超过这个时间还没返回就会触发ontimeout回调。同时网络异常、跨域拦截、服务器闪断时会触发onerror回调。这两个事件在日常开发中经常被忽略但真实场景里用户网络不稳定、服务器负载高是常态不处理这两个事件用户体验会很糟糕。参考写法var xhr new XMLHttpRequest(); xhr.timeout 10000; // 10秒超时 xhr.ontimeout function() { console.warn(请求超时了); }; xhr.onerror function() { console.error(网络错误或连接失败); };建议每一个 XHR 请求都至少设置超时时间和错误回调这是从“能跑”到“好用”的重要一步。5.4 跨域问题这是浏览器的安全策略不是后端能单方面解决的跨域这个坑很经典。假设你的前端跑在http://localhost:3000要请求http://api.example.com/data你会发现请求发出去后status是 0responseText是空字符串控制台会报No Access-Control-Allow-Origin header is present。很多初学者以为是自己代码写错了其实这是浏览器的同源策略在起作用——浏览器出于安全考虑默认禁止一个源的页面通过 AJAX 请求另一个源的数据。解决方案有几个最标准的是后端在响应头里加Access-Control-Allow-Origin: *或指定具体源老项目还常用 JSONP 绕过但 JSONP 只支持 GET 请求已经算上古时代的方案了开发环境下可以配置代理服务器转发请求。作为 AJAX 入门你只需要理解跨域的本质是浏览器拦截不是请求没发出去通常前端能做的很有限核心要靠后端配合开启 CORS。实在拿不到跨域数据但又得继续写功能时可以考虑走代理转发这条路。5.5 老项目兼容性onreadystatechange 依然是通用方案现代浏览器里xhr.onload、xhr.onerror用起来非常方便但如果你维护的老系统还面向低版本浏览器onreadystatechange这种写法反而更稳妥因为它不依赖较新的 XHR 特性。同时要注意某些老环境下xhr.responseType json可能不生效老老实实解析responseText反而最稳。这也是为什么我在前文不断强调responseTextJSON.parse这套组合的原因——兼容性最好逻辑最透明。6. 防抖与脏检查真实项目里 AJAX 请求的控制策略入门的最后一步我想聊聊真实业务中 AJAX 使用频率最高的两个控制策略防抖debounce和请求竞态处理。这两个东西严格来说不属于 XMLHttpRequest 本身但如果你不懂它们在真实项目里写 AJAX 请求大概率会被同事 review 出问题。6.1 搜索框场景防抖避免每敲一个字就发请求假设你要实现一个搜索建议功能用户每输入一个字符前端就向后端请求一次关键词联想。如果不加控制用户输入“前端开发”四个字你可能瞬间发出了四五次请求既浪费服务器资源又会让响应结果乱序。防抖的思路是用户停止输入一段时间后才真正发出请求。比如设置 300ms 的延迟用户连续输入时不断重置计时器直到暂停输入 300ms 后才发送一次请求。原生实现非常简单var timer null; input.addEventListener(input, function() { clearTimeout(timer); timer setTimeout(function() { sendAjaxRequest(input.value); }, 300); });这个 300ms 是个经验值太短起不到防抖效果太长用户会觉得卡顿。在实际项目中搜索建议一般用 200-400ms 都比较合适。6.2 响应乱序问题竞态处理不能让老响应覆盖新响应还有一个比防抖更隐蔽的坑请求竞态。用户先输入“AJAX”再输入“AJAX入门”第一次请求可能设置了 500ms 超时第二次请求反而更快返回。如果后发起的请求先返回先发起的请求后返回页面最终显示的内容就变成了旧数据覆盖新数据。这种问题用防抖只能减少发生频率不能从根本上解决。根本解法是每次发起新请求时给前一个请求打上“作废”标记或者使用请求序号判断响应是否过期。比如维护一个请求计数器只有当前请求的序号和最新序号相同时才允许更新页面数据var requestSeq 0; function search(keyword) { var currentSeq requestSeq; var xhr new XMLHttpRequest(); xhr.open(GET, /api/search?kw encodeURIComponent(keyword), true); xhr.onreadystatechange function() { if (xhr.readyState 4 xhr.status 200) { if (currentSeq ! requestSeq) { // 说明不是最新的请求忽略结果 return; } renderList(JSON.parse(xhr.responseText)); } }; xhr.send(); }这段代码的思路不复杂但非常实用。类似的思路在如今 axios AbortController 的时代也能找到对应实现只是底层机制不同。你掌握了这套原生的处理逻辑后再去迁移到任何框架都会容易得多。7. 从 XHR 到 fetch 和 axios弄清楚它们的关系再选型既然已经完整跑通了原生 XHR 的请求流程那不妨再看看现在前端圈子里大量使用的fetch和axios到底和 AJAX 是什么关系方便你在实际项目里做技术选型。7.1 fetch 是 XHR 的替代者但不是简单封装fetch是浏览器提供的更现代的请求 API它基于 Promise语法比XMLHttpRequest简洁很多。一个 GET 请求的写法是fetch(https://example.com/api/data) .then(function(response) { return response.json(); }) .then(function(data) { console.log(data); }) .catch(function() { console.error(请求失败); });它和 XHR 最直观的区别是不再需要手动管理readyState也不再需要通过事件回调轮询而是直接把请求的结果通过 Promise 链传递下来。但从底层来看它依然是浏览器发起的网络请求同样受同源策略限制同样有超时、缓存、Cookie 策略这些概念。所以你可以把 fetch 理解为 XHR“函数式”的升级版但不能说它彻底摆脱了 XHR 背后的网络交互模型。fetch 有一个常见坑它只有在网络错误或请求被中断时才 rejectHTTP 状态码是 404 或 500 时它不会走.catch你需要在then里手动判断response.ok。这一点和 XHR 的status判断逻辑如出一辙只是写法不同。如果你理解了 XHR 的 status 判断这点就难不倒你。7.2 axios 则是把浏览器和 Node 环境统一处理的封装库axios在浏览器环境中底层用的就是XMLHttpRequest只是它外面包了一层 Promise同时做了一堆便利的事情——比如自动序列化请求数据、自动解析响应 JSON、支持拦截器、取消请求、统一错误处理。它之所以受欢迎是因为在真实业务中我们经常需要“统一的请求配置”“统一的错误提示”“统一的拦截逻辑”XHR 和 fetch 都不会替你做这些而 axios 把这些公共逻辑抽象得很完善。回到入门角度我建议先写熟原生 XHR再迁移到 fetch最后再上手 axios。这个顺序最科学。反过来先跑通 axios 再回过头看 XHR你大概率只会复制粘贴出了错完全不知道 axios 内部做了什么。8. 一个完整的综合案例模拟登录请求与页面局部更新理论聊了不少最后做一个完整的综合案例把这一整套知识串起来。假设我们要实现一个“登录表单提交”的功能用户在页面上填写用户名和密码点击登录按钮后AJAX 把数据 POST 到服务器服务器返回一个用户信息和 token然后前端把页面上的登录区域替换成用户信息区域整个过程不刷新页面。8.1 页面结构与基础事件绑定HTML 结构大致是这样input typetext idusername placeholder用户名 input typepassword idpassword placeholder密码 button idloginBtn登录/button div idresultBox未登录/div要给这个页面接上 AJAX 请求我们先绑定按钮点击事件var btn document.getElementById(loginBtn); btn.addEventListener(click, function() { var username document.getElementById(username).value; var password document.getElementById(password).value; if (!username || !password) { alert(请填写用户名和密码); return; } doLogin(username, password); });点击事件里做用户输入校验是最基本的前端体验设计这里校验无需发请求纯前端完成。8.2 封装 doLogin 函数从创建 XHR 到局部刷新function doLogin(username, password) { var xhr new XMLHttpRequest(); xhr.open(POST, https://example.com/api/login, true); xhr.setRequestHeader(Content-Type, application/json;charsetUTF-8); xhr.timeout 8000; xhr.ontimeout function() { var box document.getElementById(resultBox); box.innerHTML 登录超时请稍后重试; }; xhr.onerror function() { var box document.getElementById(resultBox); box.innerHTML 网络异常请检查网络; }; xhr.onreadystatechange function() { if (xhr.readyState ! 4) { return; } var box document.getElementById(resultBox); if (xhr.status 200 xhr.status 300) { try { var data JSON.parse(xhr.responseText); box.innerHTML 欢迎 data.userName 你的登录凭证已生成; } catch (e) { box.innerHTML 服务器返回数据格式异常; } } else { box.innerHTML 登录失败状态码 xhr.status; } }; var body JSON.stringify({ username: username, password: password }); xhr.send(body); }这个函数包含了请求头设置、超时处理、错误处理、状态码判断、JSON 解析和 DOM 局部更新基本上是把前文所有知识点综合起来了。8.3 代码演示与模拟测试因为没有真实的后端地址你可以找一个支持请求测试的公共 mock 服务比如常见的 JSONPlaceholder 之类他们提供了假接口可以用来练习 GET/POST 请求。如果团队里有后端同学也可以让他在本地起一个测试接口。无论用哪种方式重点是我们需要确认请求发出去了、状态码是 2xx、返回的数据被正确解析、页面局部更新生效。我平时推荐学员自己动手做一套本地模拟可以用 Node 写一个十几行代码的 HTTP 服务返回一段 JSON然后本地直接调试这样既能练习前端 AJAX也能顺带看懂后端什么时候返回数据、什么时候报错理解更立体。这个训练方式我用了很多年效果比看十篇教程都好。9. 我在实际项目里对 AJAX 的几个坚持最后聊一点个人偏好。这篇文章写到这里核心知识已经全部铺开了我想分享几个在实际项目中一直坚持的习惯。第一个习惯凡是 AJAX 请求必然写完整的状态分支。也就是说不管有没有显示需求我都至少处理readyState 4下的成功、失败、异常三条路径。只写成功路径、失败就静默的代码到了生产环境排查问题时简直让人抓狂。第二个习惯请求 URL 和参数一律集中管理。哪怕是一个小页面我也会把接口地址、参数拼接逻辑单独抽出来而不是散落在各个事件回调里。原因很简单当接口地址变更或者参数结构调整时你只需要改一个地方。第三个习惯任何响应数据都先判断再解析。不管是responseText是否为空、是否能被 JSON.parse都要做防御处理。后端结构升级导致返回数据格式变化的情况太常见了前端如果不做防御用户看到的就是页面白屏或者一堆异常。第四个习惯多了解一点 HTTP 状态码。这是 AJAX 之外但切换 AJAX 必须要懂的知识。我看到很多同学拿到请求失败的结果只是把 status 打印出来却不知道 401、403、404、500 分别意味着什么。既然已经在接触 XHR 了花一小时把 HTTP 常用状态码过一遍收益会非常大。学 AJAX 的路线说长不长说短不短。核心是搞清楚“异步”这两个字背后的运行机制其次是学会通过 XHR 的属性、事件去观察一次请求的生命周期。只要这两个点通了写代码就只是形式和封装的问题。这篇文章我用的是原生 XHR 的写法每一步都拆得很细你上手写完这个登录案例之后建议自己再改造成 fetch 版本和 axios 版本对比着看三种写法的差异对浏览器网络请求模型的理解基本就到下一个层级了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑