XHR/fetch/axios 对比与差异

XHR / fetch / axios 对比与差异 · 学习资料


一、先理清关系(很多人搞混)

浏览器原生层
├─ XMLHttpRequest (1999)  ── 老牌 API,回调式
└─ fetch (ES2015)         ── 新标准,Promise 式,底层不再走 XHR

第三方库层
└─ axios                  ── 封装库
     ├─ 浏览器端:adapter = XHR        ← 所以有「上传进度」
     └─ Node 端 :adapter = http 模块

关键认知:

  • axios 不是 fetch 的替代品,也不是 XHR 的替代品 —— 它是基于 XHR 的封装(浏览器端)。
  • fetch 与 XHR 是并列的两套原生能力;axios 站在 XHR 之上。
  • 所以「axios 有上传进度、fetch 没有」这句话的根源就在这里。

二、同一需求的三种写法

需求:POST 一个 JSON,拿回数据。

① XHR

function request(url, data) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('POST', url)
    xhr.setRequestHeader('Content-Type', 'application/json')
    xhr.responseType = 'json'
    xhr.timeout = 10000
    xhr.onload = () => {
      // ⚠️ 404/500 也会进 onload,必须自己判 status
      if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.response)
      else reject(new Error(`HTTP ${xhr.status}`))
    }
    xhr.onerror = () => reject(new Error('network error'))
    xhr.ontimeout = () => reject(new Error('timeout'))
    xhr.send(JSON.stringify(data))
  })
}

② fetch

async function request(url, data) {
  const res = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
    signal: AbortSignal.timeout(10000),
  })
  // ⚠️ 404/500 不会 reject,必须自己判 res.ok
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
  return res.json()   // ⚠️ 还要再 await 一次
}

③ axios

const { data } = await axios.post(url, data, { timeout: 10000 })
// ✅ 2xx 之外的状态码自动 reject(validateStatus 可改)
// ✅ 自动 JSON 序列化/反序列化

三、核心差异总表

维度 XHR fetch axios
出身 浏览器原生(老) 浏览器原生(新标准) 第三方库
底层 — 浏览器网络栈 XHR(浏览器)/ http(Node)
API 形态 事件回调 Promise Promise + 拦截器
Promise ❌ 需手写包装 ✅ ✅
拦截器 ❌ ❌(要自己包) ✅ 开箱即用
自动 JSON ❌ ❌(两次 await) ✅
HTTP 错误自动 reject ❌ 手动判 status ❌ 手动判 res.ok ✅(可配 validateStatus)
网络错误 onerror reject reject
超时 xhr.timeout AbortSignal.timeout() timeout 配置
取消 xhr.abort() AbortController signal(v0.22+,CancelToken 已废弃)
上传进度 ✅ upload.onprogress ❌ ✅ onUploadProgress
下载进度 ✅ onprogress ⚠️ 需读流自算 ✅ onDownloadProgress
流式响应 ❌ ✅ res.body(ReadableStream) ⚠️ responseType: 'stream'(Node)
携带 cookie 同域默认带 credentials: 'same-origin'(默认)/ 'include' withCredentials: true
请求体类型 string / FormData / Blob + ReadableStream、URLSearchParams 对象自动 JSON、FormData 自动识别
响应类型 responseType .json()/.text()/.blob()/.arrayBuffer() responseType: 'json'/'blob'/'arraybuffer'/'text'/'stream'
并发/重试 自己写 自己写 自己写(或插件)
Node 支持 ❌ Node 18+ 原生 ✅
浏览器兼容 全(含 IE) 现代浏览器(IE 需 polyfill) 同 XHR,兼容性好
体积 0 0 ~13KB(gzip 后更小)
TypeScript 一般 一般 ✅ 泛型友好

四、逐项深挖(易错点)

1. HTTP 错误语义(最容易踩)

404 / 500 时
XHR 进 onload/onreadystatechange,status=500,不触发 onerror
fetch 正常 resolve,需 if (!res.ok)
axios 自动 reject(默认 validateStatus: s => s >= 200 && s < 300)

fetch 只有网络层失败(断网、DNS、CORS 被拦)才 reject。

2. JSON 处理

// fetch:两步,且 .json() 本身也可能失败(返回了 HTML 错误页)
const res = await fetch(url)
const data = await res.json()   // 可能抛 SyntaxError

// axios:一步,且自带 Content-Type 识别
const { data } = await axios.get(url)
// xhr
xhr.withCredentials = true          // 跨域带 cookie

// fetch
fetch(url, { credentials: 'include' })        // 跨域带
fetch(url, { credentials: 'same-origin' })    // 默认值,同域带

// axios
axios.get(url, { withCredentials: true })

三者都要求服务端返回 Access-Control-Allow-Credentials: true 且 Access-Control-Allow-Origin 不能为 *。

4. 超时与取消

// xhr
xhr.timeout = 5000; xhr.ontimeout = fn; xhr.abort()

// fetch(现代写法)
fetch(url, { signal: AbortSignal.timeout(5000) })
const ac = new AbortController(); fetch(url, { signal: ac.signal }); ac.abort()

// axios(推荐 signal,CancelToken 已废弃)
axios.get(url, { timeout: 5000, signal: ac.signal })

5. 取消后如何识别

// fetch → err.name === 'AbortError'
// axios → err.code === 'ERR_CANCELED'(或 axios.isCancel(err))
// xhr   → 触发 onabort,status === 0

6. 流式能力(fetch 独有优势)

const res = await fetch(url)
const reader = res.body.getReader()
while (true) {
  const { done, value } = await reader.read()
  if (done) break
  console.log(new TextDecoder().decode(value))   // AI 打字机 / 大文件分片读
}

XHR / axios 做不到(axios 只能 responseType: 'stream' 在 Node 端读流)。

7. 上传进度(XHR / axios 独有)

// xhr
xhr.upload.onprogress = e => console.log(e.loaded / e.total)

// axios
axios.post(url, form, {
  onUploadProgress: e => console.log(e.loaded / e.total),
})

// fetch:❌ 无(实验性 ReadableStream + duplex:'half' 不可用于生产)

五、选型决策树

需要上传/下载进度?
├─ 是 → axios(或原生 XHR)
└─ 否
    ├─ 需要拦截器、统一错误处理、自动 JSON、Node 同构?
    │   └─ axios(业务项目首选)
    ├─ 需要流式读取(SSE 替代、AI 流式、大文件)?
    │   └─ fetch
    ├─ 追求零依赖 / 只做几个简单请求?
    │   └─ fetch
    └─ 需要兼容 IE / 老内核 WebView?
        └─ XHR 或 axios(axios 底层就是 XHR)

一句话:

  • 业务系统 / 中后台:axios(工程化能力最全)
  • 轻量 / 边缘场景 / 需要 Stream:fetch
  • 维护老代码 / 需要底层控制:XHR

六、代码迁移对照表

需求 XHR fetch axios
发 GET xhr.open('GET', url); xhr.send() fetch(url) axios.get(url)
发 POST JSON setRequestHeader + send(JSON.stringify(d)) fetch(url,{method:'POST',headers,body:JSON.stringify(d)}) axios.post(url, d)
取响应 xhr.response / responseText await res.json() res.data
判成功 status >= 200 && < 300 res.ok 自动(失败走 catch)
设 header xhr.setRequestHeader(k,v) headers: {k:v} headers: {k:v}
超时 xhr.timeout = ms AbortSignal.timeout(ms) timeout: ms
取消 xhr.abort() ac.abort() ac.abort()(传 signal)
上传进度 xhr.upload.onprogress 无 onUploadProgress
二进制 responseType='blob' await res.blob() responseType:'blob'
带 cookie withCredentials=true credentials:'include' withCredentials:true

七、三层"错误"速查

错误类型 XHR fetch axios
断网 / DNS 失败 onerror reject(TypeError) reject(ERR_NETWORK)
HTTP 4xx/5xx onload + status resolve + res.ok=false reject
超时 ontimeout reject(TimeoutError) reject(ECONNABORTED)
取消 onabort reject(AbortError) reject(ERR_CANCELED)
JSON 解析失败 自己 try .json() 抛错 自己 try(自动转换失败会抛)

八、自检题

Q1:axios 的上传进度是从哪来的?

浏览器端 adapter 用的是 XHR,upload.onprogress 是 XHR 的能力。

Q2:fetch 请求返回 500,会进 catch 吗?

不会。只有网络层失败才 reject,500 需要 if (!res.ok) throw。

Q3:axios 请求返回 500,会进 catch 吗?

会(默认 validateStatus 只认 2xx),除非自定义 validateStatus。

Q4:现代取消请求的标准方案是什么?三者分别怎么用?

AbortController + signal;xhr 用 xhr.abort(),fetch 与 axios 都传 signal。

Q5:需要读取流式响应(如 AI 逐字输出)选哪个?

fetch(res.body.getReader())。

Q6:fetch 默认会带 cookie 吗?跨域呢?

默认 credentials: 'same-origin'(同域带);跨域需显式 'include' + 服务端 Allow-Credentials。


九、一句话记忆

XHR 是地基(有进度、回调式);fetch 是新标准(Promise、有流、没进度、错误要自己判);axios 是盖在 XHR 上的房子(拦截器、自动 JSON、自动 reject、有进度)。

posted @ 2026-10-14 10:27  寅信  阅读(8)  评论(0)    收藏  举报