Ajax、Axios、Fetch 对比

三种 HTTP 请求方式的区别与使用场景

问题

Ajax、Axios、Fetch 三种请求方式有什么区别?各自的优缺点是什么?

解答

Ajax (XMLHttpRequest)

最传统的异步请求方式,基于 XMLHttpRequest 对象。

// 原生 Ajax 请求
function ajax(url, method = 'GET', data = null) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open(method, url, true)
    
    // 设置请求头
    xhr.setRequestHeader('Content-Type', 'application/json')
    
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(JSON.parse(xhr.responseText))
        } else {
          reject(new Error(xhr.statusText))
        }
      }
    }
    
    // 错误处理
    xhr.onerror = function () {
      reject(new Error('Network Error'))
    }
    
    // 发送请求
    xhr.send(data ? JSON.stringify(data) : null)
  })
}

// 使用
ajax('/api/users', 'GET')
  .then(data => console.log(data))
  .catch(err => console.error(err))

Fetch

浏览器原生 API,基于 Promise,语法更简洁。

// 基本 GET 请求
fetch('/api/users')
  .then(response => {
    // 注意:HTTP 错误状态不会 reject
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }
    return response.json()
  })
  .then(data => console.log(data))
  .catch(err => console.error(err))

// POST 请求
fetch('/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ name: 'John' })
})
  .then(response => response.json())
  .then(data => console.log(data))

// async/await 写法
async function getUsers() {
  try {
    const response = await fetch('/api/users')
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }
    const data = await response.json()
    return data
  } catch (error) {
    console.error('Fetch error:', error)
  }
}

Axios

第三方库,基于 Promise 封装 XMLHttpRequest,功能更丰富。

// GET 请求
axios.get('/api/users')
  .then(response => console.log(response.data))
  .catch(error => console.error(error))

// POST 请求
axios.post('/api/users', { name: 'John' })
  .then(response => console.log(response.data))

// 创建实例,统一配置
const instance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  headers: { 'Authorization': 'Bearer token' }
})

// 请求拦截器
instance.interceptors.request.use(
  config => {
    // 请求前处理,如添加 token
    config.headers.Authorization = `Bearer ${getToken()}`
    return config
  },
  error => Promise.reject(error)
)

// 响应拦截器
instance.interceptors.response.use(
  response => response.data,
  error => {
    // 统一错误处理
    if (error.response?.status === 401) {
      // 跳转登录
    }
    return Promise.reject(error)
  }
)

// 并发请求
Promise.all([
  axios.get('/api/users'),
  axios.get('/api/posts')
]).then(([users, posts]) => {
  console.log(users.data, posts.data)
})

对比表格

特性Ajax (XHR)FetchAxios
类型浏览器内置浏览器内置第三方库
Promise需手动封装原生支持原生支持
请求取消支持AbortControllerCancelToken
超时设置支持需手动实现支持
拦截器支持
自动转换 JSON
错误处理手动判断仅网络错误 rejectHTTP 错误也 reject
浏览器兼容IE7+现代浏览器IE11+(需 polyfill)

Fetch 的注意事项

// 1. HTTP 错误不会 reject,需要手动检查
fetch('/api/404')
  .then(response => {
    // 即使 404,也会进入 then
    console.log(response.ok)     // false
    console.log(response.status) // 404
  })

// 2. 默认不发送 cookies
fetch('/api/users', {
  credentials: 'include' // 跨域携带 cookie
  // credentials: 'same-origin' // 同源携带 cookie
})

// 3. 取消请求
const controller = new AbortController()
fetch('/api/users', { signal: controller.signal })
controller.abort() // 取消请求

// 4. 超时实现
function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController()
  const timeoutId = setTimeout(() => controller.abort(), timeout)
  
  return fetch(url, { signal: controller.signal })
    .finally(() => clearTimeout(timeoutId))
}

关键点

  • Ajax:最原始的方式,API 繁琐,需要手动封装 Promise
  • Fetch:原生 Promise 支持,但 HTTP 错误不 reject,默认不带 cookie,无超时设置
  • Axios:功能最全,支持拦截器、自动转 JSON、请求取消、超时,但需要额外引入
  • 选择建议:简单项目用 Fetch,复杂项目用 Axios,不推荐直接用原生 Ajax