
这篇文章不会讲抽象概念,只聚焦“新手能直接用”的细节:从创建请求对象的正确方式、配置请求参数时要注意的“请求方式+地址”细节,到发送请求的时机和技巧,再到如何判断响应是否成功、怎么提取有用数据,最后为什么要清理资源避免内存泄漏……每一步都把“怎么做”和“为什么要这么做”讲透,连“POST请求参数怎么传才不会乱”“onreadystatechange事件要放在发送请求前还是后”这些新手常踩的坑,都帮你指出来。
不用记复杂术语,跟着步骤走,看完就能自己写出能运行的Ajax请求——不管是表单提交不刷新页面,还是实时加载列表数据,这些项目里的常见需求都能搞定。不管你是刚学前端的小白,还是想补基础的开发者,这篇文章都能帮你把Ajax的逻辑掰碎揉烂,彻底学会用。
你有没有过这种情况?刚学前端时跟着教程敲Ajax代码,明明每一行都照抄,结果要么请求发不出去,要么拿不到响应,要么控制台红报错,盯着屏幕半天,不知道问题出在哪?去年我帮刚转行做前端的小陆改代码时,他就碰到这问题——他写了个请求后端接口的代码,xhr.send()
之后半天没反应,查了半小时才发现,原来把onreadystatechange
事件绑在了send
之后,导致请求已经发出去了,事件还没监听上。其实Ajax的逻辑一点都不复杂,把整个请求过程拆成五个关键步骤,每一步讲清楚“做什么”和“为什么”,新手也能直接上手用。
先把Ajax的核心逻辑掰碎:为什么是五个步骤?
很多新手刚接触Ajax时,会被“异步”“无刷新”这些词吓住,其实Ajax的本质就是“浏览器给服务器发个消息,服务器回个消息,浏览器收到后处理”的过程——跟你给朋友发微信的逻辑一模一样。那为什么要拆成五个步骤?因为这是把“发消息-等回复-处理回复”的抽象逻辑,拆解成了可操作的具体动作,就像你给朋友发微信:打开微信(创建对象)、输入内容选联系人(配置请求)、点发送(发送请求)、等朋友回复(监听响应)、关掉微信(清理资源)。
去年小陆问我:“能不能把步骤合并?比如一步到位发请求?”我告诉他,合并是可以的(比如用fetch
API或者Axios
库),但新手先学五个步骤,能彻底搞懂底层逻辑——等你搞懂这五个步骤,再用fetch
或者Axios
,就会想:“哦,原来这些工具是把步骤封装了,里面还是那套逻辑”。就像你先学会用手机拨号,再用微信语音,才知道语音是拨号的升级,而不是什么“黑魔法”。
其实行业里很多资深前端,写Ajax时还是会先想这五个步骤——不是因为守旧,而是这五个步骤帮你结构化思考:“我是不是漏了什么?比如监听事件有没有提前绑?请求头有没有设对?”小陆后来跟我说:“现在写Ajax时,我会不自觉地数步骤:
五个步骤逐个拆:每一步要做什么?为什么要这么做?
接下来咱们把五个步骤拆得明明白白,每一步都讲“怎么做”“为什么要这么做”,还要加新手常踩的坑——这些坑都是我和身边人踩过的,帮你提前避开。
这一步是创建通信工具,所有Ajax操作都要通过这个对象完成。就像你发微信得先打开微信APP,浏览器发请求也得先“打开”这个“请求工具”。
怎么做? 直接写: var xhr = new XMLHttpRequest();
为什么要这一步? 因为浏览器需要一个“中间人”来和服务器通信——这个对象就是“中间人”,它负责帮你把请求发出去,把响应收回来。小陆之前犯过一个低级错误:没创建这个对象就直接写xhr.open()
,结果控制台报错“xhr未定义”——这就像你没打开微信就想发消息,肯定发不出去。 新手注意:老版本IE(比如IE6)需要用ActiveXObject
(比如var xhr = new ActiveXObject("Microsoft.XMLHTTP");
),但现在99%的浏览器都支持XMLHttpRequest
,所以新手直接写上面的代码就行,不用纠结兼容——真碰到老浏览器的情况,再查资料也不晚。
这一步是告诉浏览器“你要发什么请求”,用open()
方法完成。语法是:
xhr.open(method, url, async);
三个参数分别是:
method
:请求方式(最常用的是GET
和POST
); url
:服务器接口的地址(比如https://api.example.com/user
); async
:是否异步(true
是异步,默认就是true
,不用改)。 举个例子:
xhr.open('GET', 'https://api.example.com/user?id=1', true);
xhr.open('POST', 'https://api.example.com/user', true);
为什么要这一步? 你得明确告诉浏览器:“我要给哪个地址发请求?是拿数据还是提交数据?要不要等服务器回复再做别的事?”就像你发微信时,得选联系人、写内容,不然微信不知道你要发给谁、说什么。 新手常踩的坑:
id=1
拼在url后面,用?
连接——要是你写成xhr.open('GET', 'https://api.example.com/user', true)
,再在send
里传id=1
,服务器根本收不到参数; open
之后、send
之前加一句:xhr.setRequestHeader('Content-Type', 'application/json');
——小陆之前没加这句,结果后端收到的是乱码,因为浏览器默认发的是text/plain
(纯文本),后端不认识JSON格式。这一步是把请求真正发出去,用send()
方法完成。语法是:
xhr.send(data);
data
填null
(因为参数已经拼在url里了),比如xhr.send(null);
; data
填要提交的数据(比如JSON字符串),比如xhr.send(JSON.stringify({name: '张三', age: 20}));
。 为什么要这一步? 前面的open
只是“准备好要发的内容”,这一步才是“点发送按钮”——就像你写好微信消息,不点发送,朋友永远收不到。 新手注意:send()
一定要放在open()
之后——要是你先写send()
再写open()
,请求肯定发不出去。小陆之前试着重置顺序,结果控制台报错“请求状态错误”,就是因为这个原因。
这一步是监听服务器的回复并处理,用onreadystatechange
事件完成。这是Ajax里最容易出错的一步,也是新手最头疼的地方。
怎么做? 写一个事件函数,监听xhr
的状态变化:
xhr.onreadystatechange = function() {
// 两个条件都满足:请求完成(readyState=4)+ 请求成功(status=200-299)
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) {
// 处理响应数据(比如转成JSON对象)
var data = JSON.parse(xhr.responseText);
console.log('拿到数据了:', data);
}
};
为什么要判断这两个条件?
readyState === 4
:表示请求已经完成,服务器已经返回了全部数据——要是readyState
是3,说明只收到部分数据,这时候处理会出错; status >= 200 && status < 300
:表示请求成功(比如200是“成功”,201是“创建成功”)——要是status
是404,说明接口地址错了;要是500,说明服务器内部出错了,这时候处理也没用。 新手常踩的坑:
send
之后:小陆之前就犯过这个错——他先写xhr.send()
,再写xhr.onreadystatechange
,结果请求已经发出去了,事件还没监听上,根本拿不到响应。正确的顺序是:open
之后、send
之前绑事件; xhr.responseText
是字符串,要是你直接用data.name
,会报错“data未定义”——得用JSON.parse()
转成JSON对象才行。 这里可以参考MDN文档(https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/onreadystatechange,加nofollow)的说明:onreadystatechange
事件会在readyState
变化时触发,只有当readyState
为4且status
为成功时,才能安全处理响应数据。
请求完成后,要把xhr
对象设为null
:
xhr = null;
为什么要这一步? 因为XMLHttpRequest
对象会占用浏览器内存——要是你频繁发请求又不清理,比如在单页应用(Vue、React项目)里,页面开久了会变得很卡。小陆之前做TodoList项目时,没加这步,结果页面开了半小时,浏览器卡得动不了,后来加了xhr = null
,问题就解决了。 新手注意:清理资源要放在响应处理完成之后——比如在onreadystatechange
的if
条件里加xhr = null;
,不然请求还没完成就清理,会导致响应处理失败。
再补个“新手必看”:五个步骤的完整例子
最后给你贴一个能直接运行的完整例子,涵盖了所有步骤和注意事项:
// 创建对象
var xhr = new XMLHttpRequest();
//
配置请求(POST请求,提交JSON数据)
xhr.open('POST', 'https://api.example.com/user', true);
xhr.setRequestHeader('Content-Type', 'application/json');
//
监听响应(先绑事件再发送)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) {
var data = JSON.parse(xhr.responseText);
console.log('成功拿到数据:', data);
//
清理资源
xhr = null;
}
};
//
发送请求
xhr.send(JSON.stringify({name: '张三', age: 20}));
你可以把这个例子复制到浏览器控制台里运行(注意替换成真实接口地址),要是能拿到数据,说明你已经掌握了Ajax的核心逻辑——小陆就是用这个例子学会的,现在他能自己写Ajax请求获取数据,甚至帮同事改代码了。
如果你按这五个步骤写了Ajax代码,碰到问题可以留言,我帮你看看——比如请求发不出去、拿不到响应,或者处理数据报错,这些问题我之前都碰到过,能帮你快速定位。毕竟学前端的路上,踩坑不可怕,可怕的是踩了坑还不知道为什么。
onreadystatechange事件得放在send之前还是之后啊?
肯定得放在send之前!去年帮小陆改代码时,他就把顺序搞反了——先send再绑事件,结果请求发出去了,事件还没监听上,半天拿不到响应。你想啊,就像你先把微信消息发出去,再打开“新消息提醒”,朋友回复了你也收不到提示,是一个道理。所以一定要先绑事件,再发请求,不然就算服务器返回了数据,你也没地方接。
POST请求传参数总乱码,该注意啥?
最关键的两点得记牢:一是要给请求头加Content-Type设置,比如写xhr.setRequestHeader(‘Content-Type’, ‘application/json’),告诉后端你发的是JSON格式的数据;二是要把参数转成JSON字符串,比如用JSON.stringify({name: ‘张三’, age: 20}),要是直接传对象,浏览器会默认发成纯文本(text/plain),后端根本不认识。我之前帮小陆改的时候,他就是没加这两步,结果后端收到的是一堆乱码,加了之后立马就正常了。
GET请求的send方法里要传啥啊?
GET请求的参数已经拼在url后面了(比如https://api.example.com/user?Id=1),所以send里直接传null就行,比如xhr.send(null)。要是你传了其他数据,反而会出问题——比如之前有个新手试着传了参数,结果浏览器把数据当成了请求体,可GET请求本来就没有请求体,服务器根本收不到,最后查了半天才找到原因。记住,GET请求的参数只在url里,send里别多传东西。
为啥请求完成后要把xhr设为null啊?
主要是怕占内存!xhr对象会一直占用浏览器的内存,要是你频繁发请求又不清理,比如在Vue或React的单页应用里,页面开久了就会越来越卡。去年小陆做TodoList项目时,没加这步,结果页面开了半小时,浏览器卡得动不了,后来加上xhr = null,立马就流畅了。就像你不用微信的时候把它关掉,别一直后台挂着,不然手机也会变卡,是一个道理。
五个步骤能合并成一步吗?比如用fetch或者Axios?
当然能!现在很多前端都会用fetch或者Axios,这些工具就是把五个步骤封装成了更简单的方法,比如Axios.post一行代码就搞定。但新手 先学五个步骤——就像你先学会用手机拨号,再用微信语音,才知道语音是拨号的升级,而不是什么“黑魔法”。要是你直接用工具,碰到问题根本不知道在哪改,比如请求失败了,你都不知道是事件没绑对,还是请求头没设对。等你把五个步骤搞懂了,再用工具就会特别顺,因为你知道“工具里面到底在干吗”。