Skip to content

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,可以显式设置:

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 配合
  • 请求 / 响应模型更现代
  • 更适合和 AbortControllerRequestResponse 协作

什么时候仍然会用 XHR

  • 老项目已有成熟封装
  • 需要上传 / 下载进度事件
  • 某些历史兼容性要求

和第三方库的关系

很多老一代请求库或封装层,本质上都构建在 XHR 之上。

例如:

  • jQuery.ajax
  • 浏览器端的早期请求封装
  • 某些旧版上传组件

基于 MIT 许可发布