主题
XMLHttpRequest
XMLHttpRequest,通常简称 XHR,是浏览器里更早一代的网络请求接口。在 fetch 普及之前,大多数 AJAX 请求都基于它实现。
XHR 的定位
XHR 的特点主要有:
- 浏览器原生提供
- 基于事件和回调处理请求状态
- API 相对底层
- 老项目中依然非常常见
虽然现在多数新项目优先使用 fetch,但理解 XHR 仍然有价值,尤其是在处理上传下载进度、阅读旧代码和排查底层请求问题时。
一个最基础的 GET 请求
js
const xhr = new XMLHttpRequest()
xhr.open('GET', '/api/users', true)
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) {
return
}
if (xhr.status >= 200 && xhr.status < 300) {
console.log(JSON.parse(xhr.responseText))
return
}
console.error(`HTTP ${xhr.status}`)
}
xhr.onerror = () => {
console.error('network error')
}
xhr.send()一个最基础的 POST 请求
js
const xhr = new XMLHttpRequest()
xhr.open('POST', '/api/users', true)
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) {
return
}
if (xhr.status >= 200 && xhr.status < 300) {
console.log(JSON.parse(xhr.responseText))
}
}
xhr.send(
JSON.stringify({
name: 'Tom',
}),
)readyState 是什么
XHR 会经历几个状态阶段:
0:未初始化1:已调用open()2:已收到响应头3:响应体接收中4:请求完成
日常代码里最常见的是在 readyState === 4 时统一处理结果。
常用能力
设置请求头
js
xhr.setRequestHeader('Authorization', `Bearer ${token}`)获取响应文本
js
console.log(xhr.responseText)取消请求
js
xhr.abort()上传和下载进度
这是 XHR 相比 fetch 仍然常见的一个原因。
上传进度
js
xhr.upload.onprogress = (event) => {
if (!event.lengthComputable) return
console.log(event.loaded / event.total)
}下载进度
js
xhr.onprogress = (event) => {
if (!event.lengthComputable) return
console.log(event.loaded / event.total)
}跨域携带 Cookie
如果跨域请求需要携带 Cookie,可以显式设置:
js
const xhr = new XMLHttpRequest()
xhr.open('GET', 'https://api.example.com/user', true)
xhr.withCredentials = true
xhr.send()同时服务端还要正确配置 CORS 和凭证响应头。
为什么现在更多使用 Fetch
相比 XHR,fetch 的优势通常有:
- Promise 风格更清晰
- 更容易和
async/await配合 - 请求 / 响应模型更现代
- 更适合和
AbortController、Request、Response协作
什么时候仍然会用 XHR
- 老项目已有成熟封装
- 需要上传 / 下载进度事件
- 某些历史兼容性要求
和第三方库的关系
很多老一代请求库或封装层,本质上都构建在 XHR 之上。
例如:
jQuery.ajax- 浏览器端的早期请求封装
- 某些旧版上传组件
