主题
XMLHttpRequest 基本使用
本页对应 1-7XMLHttpRequest基本使用和案例,共 5 段视频,约 46 分钟。
学习目标
- 能独立使用 XMLHttpRequest 完成“实例化对象、open 设置请求方法与 URL、监听 loadend 事件、send 发起请求”四步,并从 response 获取服务器返回数据。
- 能识别 XMLHttpRequest 返回的 JSON 字符串,并使用 JSON.parse 转换为 JavaScript 对象后再使用。
- 能按 URL 查询参数格式手动拼接
?key=value&key=value,并将查询参数正确附加到请求地址中。 - 能使用 URLSearchParams 将 JavaScript 对象转换为查询参数字符串,并说明中文字符被编码不影响服务器解析。
- 能在地区查询案例中获取输入框值、调用接口、解析返回数据,并使用 map 与 join 生成 HTML 结构后渲染到页面。
- 能使用 XMLHttpRequest 提交 JSON 数据,设置
Content-Type: application/json,并通过JSON.stringify将对象转换为 JSON 后放入 send 方法发送。
知识点
AJAX 与 XHR:为什么要学原生对象
为什么需要:AJAX 是浏览器与服务器通信的技术,平时用的 axios 是对 XHR 相关代码的封装。学会 XHR,既能理解 axios 内部如何与服务器交互,也能在没有 axios 时直接完成通信。
怎么想:AJAX 原理不是某个框架,而是浏览器提供的 window.XMLHttpRequest 对象。先创建 XHR 对象,再按固定步骤发请求、收结果。
怎么做:用构造函数创建对象:
js
const xhr = new XMLHttpRequest()易错点:构造函数名是 XMLHttpRequest,拼写较长,写错时编辑器可能没有提示;不要把 XHR 和 axios 混为一谈,axios 只是封装。
XHR 四步法:语法与思路
为什么需要:XHR 的用法步骤固定,记住四步就能套用到不同接口。
怎么想:创建对象 → 调用 open 设置请求方法和 URL → 监听 loadend 事件接收结果 → 调用 send 发起请求。请求方法和 URL 要从接口文档获取,不能乱写。

怎么做:
js
const xhr = new XMLHttpRequest()
xhr.open('GET', '接口文档中的省份列表URL')
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
})
xhr.send()易错点:open 只是设置方法和地址,真正发请求是 send;事件名是 loadend,不是 load end;结果从 xhr.response 取,不是随便定义的变量。
需求实战:获取省份列表并展示
为什么需要:只看语法容易没感觉,用“获取所有省份列表并展示到页面上”验证四步法。
怎么想:结合需求,只需要改三处:请求方法、请求地址、拿到数据后的使用逻辑。其余创建对象、监听事件、发送请求的骨架不变。
怎么做:先输出 xhr.response 确认数据。转写稿中的调试过程是:网络面板看到请求发出,预览里 list 后面是省份数组;控制台也输出了结果。但此时 response 是 JSON 字符串,不是对象,所以要用 JSON.parse 转换,再取 list 渲染:
js
xhr.addEventListener('loadend', () => {
const data = JSON.parse(xhr.response)
// 选择器以页面中用于展示的 p 标签为准
document.querySelector('p').innerText = data.list
})易错点:原生 XHR 不会自动把 JSON 转成对象,必须自己 JSON.parse;渲染纯文本用 innerText,转写稿中明确这里不考虑解析 HTML 标签;取数据时注意属性是 list。
调试与验证:从网络面板到控制台
为什么需要:确认请求是否发出、服务器是否返回、页面是否渲染,不能只看代码“觉得对”。
怎么想:先看网络面板的请求和预览,再看控制台输出,最后看页面展示。转写稿中的顺序是:保存 → 切到浏览器 → 网络面板确认请求 → 预览看到“获取省份成功”和 list 数组 → 控制台看到输出 → 页面出现省份。
怎么做:若控制台输出的是 JSON 字符串,先 JSON.parse 再输出对象;确认对象里有 list 后,再写入页面。若页面没变化,检查 loadend 回调是否执行、选择器是否选到元素、innerText 是否赋值。
易错点:忘记 send 请求不会发出;JSON.parse 漏写会得到字符串;选择器写错或类名没加会导致渲染失败;不要把 response 当成已经转换好的对象。
小结与易错点清单
XHR 基础使用就四步:创建对象、open 设置方法和 URL、监听 loadend 接收并使用数据、send 发请求。实战中额外注意:xhr.response 是字符串时要 JSON.parse;渲染文本用 innerText;请求方法和地址以接口文档为准。掌握这些,再看 axios 的封装会更容易理解。
先分清:XHR 和查询参数各解决什么问题
为什么需要:查询参数的作用,是浏览器把额外信息带给服务器,服务器据此返回匹配的数据。之前用 axios 时,我们传一个对象,axios 会自动把它拼成查询参数;现在改用原生 XMLHttpRequest(转写稿里口误成“小黄人对象”),没有这层封装,必须自己拼。
怎么想:查询参数放在 URL 上,格式是 url?参数名=值&参数名=值。而 XHR 设置 URL 的地方,就是 open 方法的第二个参数,所以拼接点就在 open 里。
怎么做:先照接口文档确认请求方法、地址和参数名。本节查城市列表,参数名是 pname,表示要查哪个省的城市。示例沿用转写稿演示的湖北省;换成河北省,只需改参数值。
易错点:? 只能出现一次,多个参数之间用 &;参数名不能自己起,必须照文档;不要把参数拼到 open 的第一个参数里。
四步骨架:在 open 的 URL 后拼接查询参数
为什么需要:XHR 的核心用法是固定四步,查询参数只影响第二步的 URL,其他步骤不变。先把骨架写对,再谈拼参数。
怎么想:第一步实例化;第二步 open 设置方法和 URL;第三步监听 loadend;第四步 send 发请求。GET 请求没有请求体,所以 send() 里不传东西。
怎么做:
js
const xhr = new XMLHttpRequest()
xhr.open('GET', '接口地址?pname=湖北省')
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
})
xhr.send()易错点:事件名是 loadend,不是 loadend 拼错;response 拿到的先是字符串;URL 末尾不要多写空格;open 的方法名建议写大写 GET,虽然大小写不敏感,但统一更稳。
调试:先确认参数真的到了服务器
为什么需要:查询参数拼错时,代码不一定报错,但服务器返回的数据会不对,所以必须看网络请求。
怎么想:打开开发者工具 Network,点开请求,先看请求 URL 或查询字符串里有没有 pname=湖北省,再看 Preview 里的 message 和 list。
怎么做:保存代码后刷新页面,在 Network 中找到请求;确认查询参数已提交;Preview 中看到“获取城市成功”,list 后面是城市数组,说明参数传递成功。
易错点:不要只看控制台输出,控制台只能证明回调执行了;要确认请求 URL 里的参数名和值;如果参数名写成 pName、pname 不一致,服务器可能查不到。
拿到的是 JSON 字符串:先 parse 再渲染
为什么需要:原生 XHR 不会像 axios 那样自动把响应转成对象。转写稿中 xhr.response 打印出来是 JSON 字符串,直接当对象用会不方便。
怎么想:先用 JSON.parse(xhr.response) 转成 JS 对象,再取 data.list,最后渲染到页面上的 P 标签。
怎么做:
js
xhr.addEventListener('loadend', () => {
const data = JSON.parse(xhr.response)
document.querySelector('.city').innerText = data.list
})易错点:JSON.parse 的括号别漏;确认接口返回的确实是 JSON;innerText 和 innerHTML 不要混;示例直接把数组赋给 innerText,浏览器会转成字符串,若要逐项生成标签,需要后续用 map 和 join。
小结检查清单
- XHR 携带查询参数:在
open的 URL 后按?参数名=值&参数名=值拼接。 - 参数名和值照接口文档写,多个参数用
&。 - 先用 Network 确认参数到达服务器,再处理响应。
- 原生 XHR 的
response是字符串,使用前记得JSON.parse。 - 查询参数改变的是“查什么”,四步骨架不变。
本节目标:使用 XHR 完成案例地区查询。页面中输入省份和城市名字,点击查询后,把多对查询参数传给服务器,拿到地区列表并渲染。效果如下:

需求拆解:为什么不用 axios,而用 XHR 再做一遍
为什么需要:前面用 axios 做地区查询时,axios 会自动帮我们把参数对象转成查询字符串,也会自动把响应 JSON 转成对象。现在改用原生 XHR,就要自己处理这些细节,才能理解 axios 内部大致做了什么。
怎么想:先按 XHR 四步走:创建对象、open 设置方法和地址、监听 loadend、send 发请求。案例额外多一步:点击查询时先生成查询参数,再拼到 URL 后面。
怎么做:先取输入框的值,生成查询参数,再调用接口。调试时先打印省份和城市值,确认取到的是 北京、北京市,再去看网络面板里的查询参数。
易错点:XMLHttpRequest 单词不要写错;事件名是 loadend,不是 load end;send() 不要漏;open 的 URL 里要先有 ? 再拼参数。
生成查询参数:URLSearchParams 的作用
为什么需要:本案例有 pname、cname 两对参数,手写 pname=北京&cname=北京市 容易漏 &、漏等号。URLSearchParams 可以把参数对象快速转成“参数名=值&参数名=值”的字符串。

怎么想:参数名要按接口文档来,本案例是 pname 和 cname;值来自两个输入框。先把它们放进对象,再交给 URLSearchParams,最后调用 toString()。
怎么做:核心代码是:
js
const pname = provinceEl.value;
const cname = cityEl.value;
const params = new URLSearchParams({ pname, cname });
const query = params.toString();
console.log(query);调试时先打印 query,会看到中文被编码,例如 pname=%E5%8C%97%E4%BA%AC...。这不是错误,服务器可以正常解析,不需要额外处理。
易错点:URLSearchParams 接收的是对象,不是随便一个字符串;参数名不能写错;不要忘记调用 toString() 拿字符串结果。
调用接口与渲染数据:XHR 的完整流程
为什么需要:查询参数生成后,要拼到接口地址后面,再用 XHR 发 GET 请求。服务器返回的是 JSON 字符串,不能直接当对象用。
怎么想:在点击事件里完成三件事:生成 query、用 XHR 请求 接口地址?${query}、在 loadend 回调里解析并渲染。转写稿中的调试顺序是:先看网络面板的“载荷/查询参数”,再看“预览”确认 list 数组,最后在控制台看 xhr.response,发现它是 JSON 字符串,所以用 JSON.parse 转成对象。
怎么做:示例核心代码:
js
const provinceEl = document.querySelector('.province');
const cityEl = document.querySelector('.city');
const btn = document.querySelector('.sel-btn');
const listEl = document.querySelector('.area-list'); // 按实际页面列表父元素替换
btn.addEventListener('click', () => {
const pname = provinceEl.value;
const cname = cityEl.value;
const params = new URLSearchParams({ pname, cname });
const query = params.toString();
const xhr = new XMLHttpRequest();
xhr.open('GET', `接口地址?${query}`);
xhr.addEventListener('loadend', () => {
const data = JSON.parse(xhr.response);
const html = data.list.map(item => `<li>${item}</li>`).join('');
listEl.innerHTML = html;
});
xhr.send();
});渲染时,data.list 是地区数组,用 map 生成多个 <li>,再用 join('') 拼成一个字符串,最后通过 innerHTML 放到列表父元素中。转写稿里还验证了换城市查询,例如北京查北京市、湖北查武汉市、广东查广州市,列表都能随查询结果变化。
易错点:xhr.response 是字符串,直接 data.list 会拿不到,要先 JSON.parse;map 返回数组,必须 join('') 再赋给 innerHTML;URL 拼接别漏 ?;open 里的方法一般用 GET;渲染容器选择器要按实际页面写,不要选错父元素。
小结:JS 对象如何转成查询参数字符串
笔记小结的答案是:在调用 open 方法时,在 URL 的 ? 后面按照“参数名=值&参数名=值”的格式拼接。更推荐的做法是使用 URLSearchParams:先传参数对象,再调用 toString()。这样既减少手写拼接错误,也方便处理中文编码。整个案例的重点就是:先取输入值生成查询参数,再用 XHR 发请求,最后把 JSON 字符串转成对象并渲染列表。
目标与场景:用原生 XHR 完成注册提交
为什么需要:前面调接口多用 axios,它帮我们处理了请求头、对象转 JSON 等细节。本节要回到浏览器内置的 XMLHttpRequest(转写稿里口述为“小黄人对象”),在没有 axios 帮忙的情况下,自己完成“提交用户名和密码到服务器”的注册功能。
怎么想:注册接口不是“只要发请求”就行。接口文档里写明:请求方法是 POST,参数位置是 body,数据格式是 application/json。因此除了实例化、open、监听 loadend、send 之外,还要额外做两件事:设置请求头、准备符合要求的请求体。
怎么做:先明确本节的核心差异:原生 XHR 需要自己设置 Content-Type: application/json,告诉服务器“我发过去的内容类型是 JSON 字符串”;同时要自己把 JS 对象转成 JSON 字符串。
易错点:不要以为所有接口都要提交数据;只有文档要求 body 参数时才需要本节这两步额外操作。请求方法、地址、字段名都要看接口文档,不能随手写。
为什么数据提交要多两步
为什么需要:使用 axios 时,Content-Type 和对象转 JSON 往往不用我们额外操心;现在用原生 XHR,浏览器不会替我们完成这些“业务格式约定”。
怎么想:接口文档中的 application/json 有两层含义:一是请求头要声明数据格式;二是请求体里真正携带的数据也应是 JSON 字符串。两者要一致。
怎么做:调用 xhr.setRequestHeader('Content-Type', 'application/json') 设置请求头;再用 JSON.stringify(obj) 把 JS 对象转成 JSON 字符串,最后交给 send。
易错点:只设置了请求头,却 send 了不符合要求的数据;或者只转了 JSON,却忘记设置请求头。两者缺一不可。
怎么做:五步代码与请求体位置
为什么需要:原生 XHR 提交数据的流程比普通 GET 多了“设置请求头”和“在 send 中携带请求体”两步。
怎么想:整体仍是五步:实例化对象 → 设置请求方法和地址 → 监听 loadend 接收并使用数据 → 设置请求头 → send 发送请求体。笔记也强调:原生 XHR 需要在 send 方法调用时传入请求体。

怎么做:按接口文档复制注册接口的方法和地址;字段名按文档写。推荐先写 JS 对象,再 JSON.stringify,因为手写 JSON 容易多写或漏写符号。
js
const xhr = new XMLHttpRequest();
xhr.open('POST', '接口文档中的注册地址');
xhr.addEventListener('loadend', () => {
console.log(xhr.response);
});
// 告诉服务器:请求体是 JSON 字符串
xhr.setRequestHeader('Content-Type', 'application/json');
// 推荐:先写对象,再转 JSON 字符串
const obj = {
username: 'IT黑马999',
password: '123456'
};
const data = JSON.stringify(obj);
// 请求体在 send 中携带
xhr.send(data);易错点:send 里不要随手写一个不符合接口要求的内容;字段名要照文档,例如用户名、密码字段不能自己改名。JSON.stringify 的方法名要写对,转完后再放入 send。
调试过程:看标头、载荷和预览
为什么需要:提交代码写完不代表成功,要到浏览器网络面板确认请求头、请求体和服务器返回。
怎么想:先看请求是否发出;再看请求头里的 Content-Type 是否为 application/json;再看载荷(Payload)里的原始数据是否为 JSON;最后看预览中的返回结果。
怎么做:点击注册后,在网络面板找到请求。查看“标头”中的请求头,确认 Content-Type: application/json;查看“载荷”,必要时点“查看源代码”,确认请求体是 JSON 字符串;查看“预览”,第一次应提示注册成功。再次点击同一账号,转写稿中服务器返回“账号被占用”,说明接口确实收到并处理了提交数据。
易错点:只看控制台输出,不看网络面板;把“载荷”里浏览器格式化后的对象当成原始请求体。要确认原始数据格式与设置一致。
小结与易错点
为什么需要:本节的目标是通过 XHR 提交用户名和密码,完成注册功能。
怎么想:XHR 如何提交请求体数据?在 send 中携带请求体数据,并按照后端要求的内容类型携带。没有 axios 时,请求头要自己设,JS 对象要自己转 JSON 字符串。
怎么做:记住五步:实例化、open、监听 loadend、setRequestHeader、send。接收返回数据时仍可用 xhr.response;若后续要操作数据,按前节方式用 JSON.parse 转成 JS 对象。
易错点:忘记 setRequestHeader;Content-Type 值写错;send 中传错格式;字段名不按文档;重复注册时看到“账号被占用”就以为代码错了,其实要结合接口业务提示判断。
课件图示





代码示例
示例 1:XHR 基本使用 —— 获取省份列表并渲染
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>XHR 基本使用</title>
</head>
<body>
<p class="province"></p>
<script>
// 1. 创建 XHR 对象(构造函数有提示,注意单词别写错)
const xhr = new XMLHttpRequest()
// 2. 设置请求方法与 URL(方法、地址以接口文档为准)
xhr.open('GET', '/api/province')
// 3. 监听 loadend 事件,接收并使用数据
xhr.addEventListener('loadend', () => {
// 原生 XHR 不会自动把 JSON 转成对象,必须自己转换
const data = JSON.parse(xhr.response)
// data.list 是省份数组,直接渲染成文本
document.querySelector('.province').innerText = data.list
})
// 4. 发起请求
xhr.send()
</script>
</body>
</html>关键行 / 易错点
new XMLHttpRequest()是浏览器内置构造函数,XMLHttpRequest大小写必须完全一致。- 四步顺序固定:创建对象 → open 设置方法与地址 → 监听 loadend → send 发请求,
send必须在最后。 - 事件名是
loadend(不是load/loaded),请求结束(成功或失败)都会触发。 xhr.response拿到的是字符串,axios 会自动转换,原生 XHR 不会 —— 一定要JSON.parse后再用,否则data.list是undefined。open的两个参数必须照接口文档写,不能自己编。
示例 2:查询参数 + URLSearchParams(地区查询)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>地区查询</title>
</head>
<body>
<input type="text" class="province" value="北京市" />
<input type="text" class="city" value="北京市" />
<button class="sel-btn">查询</button>
<ul class="list"></ul>
<script>
document.querySelector('.sel-btn').addEventListener('click', () => {
// 1.1 获取输入框的值
const pname = document.querySelector('.province').value
const cname = document.querySelector('.city').value
// 1.2 用 URLSearchParams 快速生成查询参数
// 中文会被编码,但不影响服务器解析,无需额外处理
const params = new URLSearchParams({ pname, cname })
const query = params.toString() // pname=xxx&cname=xxx
// 2. 调用接口:查询参数拼在 url 后面,用 ? 分隔
const xhr = new XMLHttpRequest()
xhr.open('GET', `/api/area?${query}`)
xhr.addEventListener('loadend', () => {
const data = JSON.parse(xhr.response)
// 3. 渲染数据:map 生成多个 li,再用 join('') 拼成一整段
const html = data.list.map((item) => `<li>${item}</li>`).join('')
document.querySelector('.list').innerHTML = html
})
xhr.send()
})
</script>
</body>
</html>关键行 / 易错点
- 查询参数格式:
url?参数名=值&参数名=值,问号只写一次,多个参数之间用&连接。 - 原生 XHR 没有 axios 帮你把对象转查询串,这里用
new URLSearchParams(对象)+.toString()生成;对象的键名必须和接口文档的参数名一致(如pname、cname)。 URLSearchParams会把中文编码成一串%E5%8C%97...,这是正常现象,不要手动解码。- 拼 URL 时容易漏写
?,或把模板字符串的反引号写成单引号,导致参数没有拼上。 map之后必须join(''),直接渲染数组会带逗号。- 用
innerHTML才能解析标签;只想渲染纯文本时用innerText。
示例 3:XHR 数据提交(注册,设置请求头 + JSON 字符串)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>注册(XHR 提交数据)</title>
</head>
<body>
<button class="btn">注册</button>
<script>
document.querySelector('.btn').addEventListener('click', () => {
// 1. 创建 XHR 对象
const xhr = new XMLHttpRequest()
// 2. 设置请求方法与地址(以接口文档为准)
xhr.open('POST', '/api/register')
// 3. 监听事件,接收结果
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
})
// 4. 设置请求头:告诉服务器请求体是 JSON 格式
xhr.setRequestHeader('Content-Type', 'application/json')
// 5. 请求体:推荐先写 JS 对象,再用 JSON.stringify 转成 JSON 字符串
const data = JSON.stringify({
username: 'itheima888',
password: '123456'
})
xhr.send(data)
})
</script>
</body>
</html>关键行 / 易错点
- 相比 GET,提交数据多了两步:
setRequestHeader设置请求头、send(请求体)携带数据。 setRequestHeader('Content-Type', 'application/json')必须在open之后、send之前调用,否则不生效。- 请求体的格式要和
Content-Type一致:声明了application/json,send里就只能是 JSON 字符串。 - 绝对不要
xhr.send({ username: 'xx' })直接传对象 —— 会被转成[object Object],后端解析失败。 - 推荐
JSON.stringify(对象),而不是手写 JSON 字符串(容易多逗号、漏引号)。 - 请求体里的字段名(
username、password)必须照接口文档写,不能随手起名。
常见错误
1. 拿到 xhr.response 直接当对象用
- 现象:
xhr.response.list是undefined,控制台打印出来是一大串字符串。 - 原因:
responseType默认为空,原生 XHR 不会像 axios 那样自动JSON.parse,返回的是原始文本。 - 修法:
const data = JSON.parse(xhr.response)之后再取属性;或在open之后设置xhr.responseType = 'json',让浏览器帮你转换(此时response直接是对象,就不能再JSON.parse了,二选一,别叠加)。
2. 监听事件名写成 onloadend / onload
- 现象:请求在网络面板里明明发出去了,回调函数一次都不执行。
- 原因:
addEventListener接收的是事件名,不带on;带on的是给xhr.onloadend = fn这种赋值写法用的。 - 修法:
xhr.addEventListener('loadend', fn),或统一改用xhr.onloadend = fn,两种风格不要混着记。
3. setRequestHeader 位置放错
- 现象:浏览器抛
InvalidStateError: Failed to execute 'setRequestHeader'。 - 原因:请求头只能在
open()之后、send()之前设置,此时状态才是OPENED。 - 修法:严格按
new → open → setRequestHeader → send的顺序写;同理,responseType也必须在send之前设置。
4. POST 提交后端收不到数据 / 报参数为空
- 现象:接口返回 400 或“用户名不能为空”,但
send里明明传了东西。 - 原因:一是没设
Content-Type: application/json,XHR 默认按text/plain发,后端按 JSON 解析失败;二是直接把 JS 对象塞进send,实际发出去的是[object Object]。 - 修法:
xhr.setRequestHeader('Content-Type', 'application/json')+xhr.send(JSON.stringify(obj)),两步缺一不可。
5. 手拼查询参数漏了 ? 或 &
- 现象:后端收到的参数名变成
pname值变成北京cname=北京市,或直接 404。 - 原因:
url?参数名=值&参数名=值的分隔符是硬性格式,少一个符号整串就错位;中文和特殊字符还需要编码。 - 修法:参数超过一个就别手拼,用
new URLSearchParams({ pname, cname }).toString()一次生成;注意它产出的中文是%E5%8C%97%E4%BA%AC形态,这是正常的,服务端会解码。
6. 只写了四步的前三步,忘了 send()
- 现象:代码不报错,回调不触发,网络面板里没有任何请求。
- 原因:
open只是配置请求,send才真正把请求发出去。 - 修法:把“
send是必写的一步”当成肌肉记忆;调试时先看网络面板有没有请求,没有就是没send。
练习
练习 1|最小闭环 用原生 XHR 请求省份列表接口,把结果渲染到页面 <ul> 中。要求:用 loadend 监听、显式 JSON.parse、渲染前判断 xhr.status === 200,失败时在页面提示“加载失败”。
练习 2|参数生成对比 给定 { pname: '湖北省', cname: '武汉市' }: ① 手写拼接出完整查询字符串; ② 用 URLSearchParams 生成并 console.log 对比两者差异; ③ 说明为什么 ② 打印出来的中文“看起来不对”却仍然能用。
练习 3|注册提交 用 XHR 完成注册接口调用,写出完整的 5 步代码。要求:请求头正确、请求体由对象经 JSON.stringify 转换、回调中根据返回的 message 字段分别提示“注册成功”和“账号被占用”。
练习 4|找错 下面代码有 3 处问题,指出并改正:
js
const xhr = new XMLHttpRequest()
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.open('post', '/api/register')
xhr.addEventListener('onloadend', () => {
const data = xhr.response
console.log(data.message)
})
xhr.send({ username: 'itheima', password: '123456' })练习 5|封装 写一个 request({ method, url, data }) 函数,内部用 XHR 实现,返回 Promise。要求:GET 时用 URLSearchParams 拼参数、POST 时自动设 Content-Type 并 JSON.stringify、状态码非 2xx 时 reject。写完后回答:这个函数替你省掉了哪几件原本要手写的事?
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | XMLHttpRequest-基本使用 | 09:04 |
| 2 | XMLHttpRequest-查询参数 | 07:16 |
| 3 | 案例-地区查询-生成查询参数 | 09:29 |
| 4 | 案例-地区查询-调用接口及数据渲染 | 08:06 |
| 5 | XMLHttpRequest-数据提交 | 12:36 |
小结
- 原生 XHR 与 axios 的差距集中在三件事:数据格式转换、请求头设置、参数序列化——这三件事 axios 全包了,XHR 全得你自己来。
response永远是“服务器说了算的原始文本”,除非你主动声明responseType,否则JSON.parse是不可省略的一步。open是配置、send是执行,两者之间是设置请求头和responseType的唯一合法窗口,顺序错了就是InvalidStateError。- 参数一旦超过一个,
URLSearchParams就是默认选择;手拼字符串只在演示原理时有价值,实际写就是给自己埋 bug。 - 中文被编码成
%XX不是错误,是 URL 规范;判断“参数对不对”要看网络面板的“载荷/查询”解析结果,而不是看源码里的字符串长相。 - 提交 JSON 是“请求头 + 请求体”一对配套动作,只做一半后端必然解析失败;请求体优先“先写对象再
JSON.stringify”,比手写 JSON 字符串更不容易漏逗号引号。 - 四步/五步流程重复写三遍以上就该封装成
request函数,用 Promise 包一层,后续调用体验才接近 axios。 - 排错顺序固定为:网络面板有没有请求 → 请求头/载荷对不对 → 回调有没有进 → 数据要不要转换,按这个链路走,90% 的 XHR 问题能一次定位。