Skip to content

Fetch 进阶

fetch 的基础用法并不复杂,但在真实项目里更常见的问题是:如何处理鉴权、跨域、取消请求、超时、错误状态和请求体格式。

基本结构

js
const response = await fetch('/api/users', {
  method: 'GET',
})

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`)
}

const data = await response.json()

fetch 只会在什么情况下 reject

fetch 并不会因为 404500 自动进入 catch,只有网络层失败时才会 reject,例如:

  • DNS 解析失败
  • 断网
  • 请求被主动取消
  • 浏览器策略阻止请求

所以业务代码里通常要手动检查 response.ok

js
fetch('/api/user')
  .then((response) => {
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }

    return response.json()
  })
  .catch((error) => {
    console.error(error)
  })

常用配置项

headers

js
fetch('/api/user', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
})

body

发送 JSON:

js
fetch('/api/user', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ name: 'Tom' }),
})

发送 FormData

js
const formData = new FormData()
formData.append('file', file)

fetch('/upload', {
  method: 'POST',
  body: formData,
})

发送 FormData 时,不要手动设置 Content-Type,让浏览器自动补 boundary

credentials

用于控制请求是否携带 Cookie、HTTP 认证信息等凭证。

  • same-origin:默认值,仅同源请求携带。
  • include:跨域请求也允许携带凭证。
  • omit:不携带凭证。
js
fetch('https://api.example.com/user', {
  credentials: 'include',
})

如果跨域请求要携带 Cookie,服务端还需要配合:

  • Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Origin 不能是 *

mode

控制请求模式,常见值有:

  • cors:默认跨域请求模式。
  • same-origin:只允许同源。
  • no-cors:能力非常受限,通常只适合某些上报场景。
js
fetch('https://api.example.com/data', {
  mode: 'cors',
})

cache

控制浏览器缓存策略:

  • default
  • no-store
  • reload
  • no-cache
  • force-cache
  • only-if-cached
js
fetch('/api/list', {
  cache: 'no-store',
})

redirect

控制重定向行为:

  • follow:默认,自动跟随。
  • error:遇到重定向直接报错。
  • manual:手动处理。
js
fetch('/login', {
  redirect: 'error',
})

请求取消

可结合 AbortController 使用:

js
const controller = new AbortController()

fetch('/api/list', {
  signal: controller.signal,
})

controller.abort()

超时控制

fetch 本身没有原生 timeout 选项,通常自己封装:

js
async function fetchWithTimeout(url, options = {}, timeout = 5000) {
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), timeout)

  try {
    return await fetch(url, {
      ...options,
      signal: controller.signal,
    })
  } finally {
    clearTimeout(timer)
  }
}

读取响应体

响应体只能消费一次。

js
const response = await fetch('/api/user')

const data = await response.json()
// const text = await response.text() // 再次读取会失败

常见读取方式:

  • response.json()
  • response.text()
  • response.blob()
  • response.arrayBuffer()
  • response.formData()

常见封装思路

js
async function request(url, options) {
  const response = await fetch(url, options)

  if (!response.ok) {
    const message = await response.text()
    throw new Error(message || `HTTP ${response.status}`)
  }

  return response.json()
}

使用建议

  • 统一检查 response.ok,不要只靠 catch
  • 有登录态的跨域请求,优先明确写出 credentials
  • 请求存在竞态时,配合 AbortController 取消旧请求。
  • 上传文件时直接传 FormData,不要手动拼 multipart/form-data

基于 MIT 许可发布