fetch 默认不校验HTTP状态码,404/500视为成功需手动检查res.ok;axios默认将4xx/5xx拒绝但需配置响应拦截器处理;两者上传FormData时均不可手动设置Content-Type头。
fetch 和 axios 都能发请求,但默认行为、错误处理、取消机制完全不同——别直接替换用,否则线上会丢 404 或超时失败不报错。
fetch 发请求时,404/500 不抛错,得手动检查 response.ok
这是最常踩的坑:fetch 只在网络异常(如断网、DNS 失败)时 reject,HTTP 状态码 404、500、401 全部算“成功”,then 里照样进。不检查就直接 res.json(),后面逻辑可能拿不到数据还静默失败。
- 必须加
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`) -
res.headers.get('content-type')要在res.clone()后才能多次读取,否则第二次调用返回 null - 没有内置 timeout,要靠
AbortController控制:const controller = new AbortController(); fetch(url, { signal: controller.signal }); setTimeout(() => controller.abort(), 8000);
axios 默认把 4xx/5xx 当错误,但响应拦截器里才能统一处理
axios 的 response 对象结构和原生不同,data 在 res.data,状态码在 res.status;它默认对非 2xx 状态码 reject,但这个 reject 会被 axios.interceptors.response.use 拦住——很多人没配拦截器,结果 catch 里拿到的是完整响应对象,不是错误实例。
- 全局配置超时:
axios.defaults.timeout = 10000,但单个请求可覆盖:axios.get('/api', { timeout: 5000 }) - 取消请求必须用
CancelToken(v0.22+ 已弃用)或AbortController(推荐):const controller = new AbortController(); axios.get('/api', { signal: controller.signal }); - 请求头自动带
Content-Type: application/json,但传FormData时得手动删掉:headers: { 'Content-Type': undefined }
上传文件时,fetch 要手动设 Content-Type: multipart/form-data?错,别设
如果用 FormData 构造请求体,fetch 和 axios 都**不能手动设置 Content-Type 头**——浏览器会自动生成带 boundary 的 multipart header,手动设会导致 boundary 缺失,后端解析失败(常见报错:Multipart: Boundary not found)。
- fetch 正确写法:
fetch('/upload', { method: 'POST', body: formData })(不加 headers) - axios 正确写法:
axios.post('/upload', formDa
ta, { headers: { 'Content-Type': undefined } })
- 进度监听:fetch 无原生支持,需用
XMLHttpRequest;axios 可用onUploadProgress: e => console.log(e.loaded / e.total)
真正难的不是怎么写那几行代码,而是记住哪些行为是“默认隐式发生”的:fetch 不校验 HTTP 状态、axios 自动序列化 JSON、两者对 FormData 的 header 处理逻辑相反……这些点在线上出问题时,日志里几乎不报错,只表现为你收不到数据。









