Skip to content

HTTP 请求实战代码示例

原生 XMLHttpRequest

GET 请求

javascript
const xhr = new XMLHttpRequest()
xhr.open(
  'GET',
  'https://api.example.com/data?param1=value1&param2=value2',
  true
)
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      const response = JSON.parse(xhr.responseText)
      console.log('GET 请求成功:', response)
    } else {
      console.error('GET 请求失败:', xhr.status)
    }
  }
}
xhr.send()

POST 请求(JSON 数据)

javascript
const xhr = new XMLHttpRequest()
xhr.open('POST', 'https://api.example.com/data', true)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      const response = JSON.parse(xhr.responseText)
      console.log('POST 请求成功:', response)
    } else {
      console.error('POST 请求失败:', xhr.status)
    }
  }
}
const data = { name: 'John', age: 30 }
xhr.send(JSON.stringify(data))

Fetch API

GET 请求

javascript
fetch('https://api.example.com/data?param1=value1&param2=value2')
  .then(response => {
    if (!response.ok) {
      throw new Error('GET 请求失败')
    }
    return response.json()
  })
  .then(data => {
    console.log('GET 请求成功:', data)
  })
  .catch(error => {
    console.error('错误:', error)
  })

POST 请求(JSON 数据)

javascript
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ name: 'John', age: 30 })
})
  .then(response => {
    if (!response.ok) {
      throw new Error('POST 请求失败')
    }
    return response.json()
  })
  .then(data => {
    console.log('POST 请求成功:', data)
  })
  .catch(error => {
    console.error('错误:', error)
  })

Axios

安装 Axios

bash
npm install axios

基本配置

javascript
import axios from 'axios'

const instance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
})

GET 请求

javascript
instance
  .get('/data', {
    params: {
      param1: 'value1',
      param2: 'value2'
    }
  })
  .then(response => {
    console.log('GET 请求成功:', response.data)
  })
  .catch(error => {
    console.error('GET 请求失败:', error)
  })

POST 请求

javascript
instance
  .post('/data', {
    name: 'John',
    age: 30
  })
  .then(response => {
    console.log('POST 请求成功:', response.data)
  })
  .catch(error => {
    console.error('POST 请求失败:', error)
  })

请求拦截器

javascript
instance.interceptors.request.use(
  config => {
    // 添加 token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

响应拦截器

javascript
instance.interceptors.response.use(
  response => {
    return response
  },
  error => {
    if (error.response && error.response.status === 401) {
      // Token 过期,跳转到登录页
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)

XMLHttpRequest vs Fetch vs Axios 对比

一、概述

特性XMLHttpRequestFetch APIAxios
类型原生 API原生 API第三方库
发布时间2000年左右2015年 (ES6)2014年
Promise 支持❌ 不支持✅ 原生支持✅ 原生支持
浏览器兼容性✅ 最广泛⚠️ 较新浏览器✅ 广泛(需 polyfill)
学习曲线中等简单简单

二、XMLHttpRequest 详解

优点

  1. 浏览器兼容性极佳

    • 支持所有主流浏览器,包括 IE6+
    • 无需任何 polyfill 或额外库
  2. 功能全面

    • 支持同步和异步请求
    • 可以获取请求进度(上传/下载)
    • 支持请求中断(abort)
    • 可以设置超时时间
  3. 细粒度控制

    javascript
    const xhr = new XMLHttpRequest()
    
    // 上传进度监听
    xhr.upload.onprogress = function (e) {
      if (e.lengthComputable) {
        const percent = (e.loaded / e.total) * 100
        console.log(`上传进度: ${percent}%`)
      }
    }
    
    // 下载进度监听
    xhr.onprogress = function (e) {
      if (e.lengthComputable) {
        const percent = (e.loaded / e.total) * 100
        console.log(`下载进度: ${percent}%`)
      }
    }
    
    // 超时设置
    xhr.timeout = 5000
    xhr.ontimeout = function () {
      console.error('请求超时')
    }
    
    // 请求中断
    xhr.abort()

缺点

  1. 回调地狱

    javascript
    // 典型的回调嵌套
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          // 嵌套下一个请求
          xhr2.onreadystatechange = function () {
            if (xhr2.readyState === 4) {
              // 继续嵌套...
            }
          }
        }
      }
    }
  2. API 设计老旧

    • 基于事件的模式不够直观
    • 代码冗长,可读性差
    • 需要手动解析 JSON
  3. 错误处理复杂

    javascript
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        // 需要手动判断状态码
        if (xhr.status >= 200 && xhr.status < 300) {
          // 成功
        } else {
          // 失败,但 404、500 等都会进入这里
        }
      }
    }
  4. 缺乏现代特性

    • 不支持 Promise
    • 不支持 async/await
    • 没有 interceptors(拦截器)

三、Fetch API 详解

优点

  1. 现代化 API 设计

    javascript
    // 简洁的 Promise 链式调用
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => console.log(data))
      .catch(error => console.error(error))
    
    // 支持 async/await
    async function getData() {
      try {
        const response = await fetch('https://api.example.com/data')
        const data = await response.json()
        console.log(data)
      } catch (error) {
        console.error(error)
      }
    }
  2. 标准化设计

    • 基于 Promise,符合现代 JavaScript 规范
    • Response 对象提供了丰富的处理方法
    • 支持流式处理
  3. 模块化设计

    javascript
    // Headers 对象
    const headers = new Headers()
    headers.append('Content-Type', 'application/json')
    
    // Request 对象
    const request = new Request('https://api.example.com/data', {
      method: 'POST',
      headers: headers,
      body: JSON.stringify({ name: 'John' })
    })
    
    // Response 对象
    const response = await fetch(request)
  4. 流式处理能力

    javascript
    // 处理大文件流
    const response = await fetch('https://example.com/large-file')
    const reader = response.body.getReader()
    
    while (true) {
      const { done, value } = await reader.read()
      if (done) break
      // 处理每个 chunk
      console.log('Received chunk:', value)
    }

缺点

  1. 错误处理设计争议

    javascript
    // HTTP 错误(如 404、500)不会 reject
    fetch('https://api.example.com/not-exist')
      .then(response => {
        // 即使 404,也会进入这里
        if (!response.ok) {
          throw new Error('请求失败')
        }
        return response.json()
      })
      .catch(error => console.error(error))
    
    // 只有网络错误才会 reject
    fetch('https://non-existent-domain.com').catch(error =>
      console.error('网络错误:', error)
    )
  2. 不支持超时(需要额外实现)

    javascript
    // 使用 AbortController 实现超时
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), 5000)
    
    try {
      const response = await fetch('https://api.example.com/data', {
        signal: controller.signal
      })
      clearTimeout(timeoutId)
    } catch (error) {
      if (error.name === 'AbortError') {
        console.error('请求超时')
      }
    }
  3. 不支持进度监控

    • 无法原生获取上传/下载进度
    • 需要使用其他 API 配合实现
  4. 默认不携带 Cookie

    javascript
    // 需要手动设置
    fetch('https://api.example.com/data', {
      credentials: 'include' // 跨域携带 cookie
    })
  5. 浏览器兼容性

    • IE 完全不支持
    • 部分功能需要较新的浏览器版本

四、Axios 详解

优点

  1. 开箱即用的完整功能

    javascript
    // 自动 JSON 转换
    axios.post('/api/data', { name: 'John' }).then(response => {
      // response.data 已经是解析后的对象
      console.log(response.data)
    })
    
    // 自动处理查询参数
    axios.get('/api/data', {
      params: { id: 1, name: 'John' } // 自动序列化为 ?id=1&name=John
    })
  2. 强大的拦截器系统

    javascript
    // 请求拦截器
    axios.interceptors.request.use(
      config => {
        // 统一添加 token
        config.headers.Authorization = `Bearer ${getToken()}`
        // 添加请求时间戳
        config.metadata = { startTime: Date.now() }
        return config
      },
      error => Promise.reject(error)
    )
    
    // 响应拦截器
    axios.interceptors.response.use(
      response => {
        // 计算请求耗时
        const duration = Date.now() - response.config.metadata.startTime
        console.log(`请求耗时: ${duration}ms`)
        return response
      },
      error => {
        // 统一错误处理
        if (error.response) {
          switch (error.response.status) {
            case 401:
              // 跳转登录
              break
            case 403:
              // 权限不足
              break
            case 500:
              // 服务器错误
              break
          }
        }
        return Promise.reject(error)
      }
    )
  3. 请求取消

    javascript
    // 使用 CancelToken(旧版)
    const source = axios.CancelToken.source()
    
    axios.get('/api/data', {
      cancelToken: source.token
    })
    
    // 取消请求
    source.cancel('请求被取消')
    
    // 使用 AbortController(新版,推荐)
    const controller = new AbortController()
    
    axios.get('/api/data', {
      signal: controller.signal
    })
    
    controller.abort()
  4. 并发请求

    javascript
    // 同时发起多个请求
    Promise.all([
      axios.get('/api/users'),
      axios.get('/api/posts'),
      axios.get('/api/comments')
    ]).then(
      axios.spread((users, posts, comments) => {
        console.log('用户:', users.data)
        console.log('文章:', posts.data)
        console.log('评论:', comments.data)
      })
    )
  5. 超时设置简单

    javascript
    // 全局超时
    axios.defaults.timeout = 5000
    
    // 单个请求超时
    axios.get('/api/data', {
      timeout: 10000
    })
  6. 防御型 XSRF

    javascript
    axios.defaults.xsrfCookieName = 'XSRF-TOKEN'
    axios.defaults.xsrfHeaderName = 'X-XSRF-TOKEN'
  7. 进度监控

    javascript
    // 上传进度
    axios.post('/api/upload', formData, {
      onUploadProgress: progressEvent => {
        const percent = Math.round(
          (progressEvent.loaded * 100) / progressEvent.total
        )
        console.log(`上传进度: ${percent}%`)
      }
    })
    
    // 下载进度
    axios.get('/api/download', {
      responseType: 'blob',
      onDownloadProgress: progressEvent => {
        const percent = Math.round(
          (progressEvent.loaded * 100) / progressEvent.total
        )
        console.log(`下载进度: ${percent}%`)
      }
    })

缺点

  1. 额外依赖

    • 需要安装第三方库
    • 增加打包体积(约 13KB gzipped)
  2. 过度封装

    • 某些场景下可能需要绕过 axios 的默认行为
    • 错误对象结构与原生不同
  3. 版本兼容性

    javascript
    // CancelToken 在新版本中已废弃
    // 旧代码
    const CancelToken = axios.CancelToken
    const source = CancelToken.source()
    
    // 新代码(推荐)
    const controller = new AbortController()

五、功能对比表

功能XMLHttpRequestFetch APIAxios
Promise 支持
async/await
自动 JSON 转换
请求/响应拦截器
请求取消✅ abort()✅ AbortController✅ 多种方式
超时设置✅ timeout 属性⚠️ 需额外实现✅ timeout 配置
上传进度
下载进度
流式处理
XSRF 防护
并发请求✅ Promise.all✅ 专用 API
同步请求
浏览器兼容✅ 最广⚠️ 较新✅ 广泛
包体积00~13KB

六、使用场景建议

选择 XMLHttpRequest 的场景

javascript
// 1. 需要支持老旧浏览器(IE)
// 2. 需要同步请求(不推荐,但某些场景必须)
const xhr = new XMLHttpRequest()
xhr.open('GET', '/api/data', false) // 同步请求
xhr.send()
console.log(xhr.responseText)

// 3. 需要极其细粒度的控制
// 4. 项目无法引入第三方库

选择 Fetch API 的场景

javascript
// 1. 现代浏览器环境
// 2. 简单的 CRUD 操作
// 3. 需要流式处理大文件
const response = await fetch('/api/large-data')
for await (const chunk of response.body) {
  processChunk(chunk)
}

// 4. Service Worker 中使用
// 5. 追求零依赖

选择 Axios 的场景

javascript
// 1. 企业级项目
// 2. 需要统一的请求/响应处理
// 3. 需要完善的错误处理机制
// 4. 需要请求重试、取消等高级功能
// 5. 需要上传/下载进度显示
// 6. 团队协作,需要统一的 API 调用规范

// 典型的企业级配置
const api = axios.create({
  baseURL: process.env.API_URL,
  timeout: 10000,
  withCredentials: true
})

// 统一请求处理
api.interceptors.request.use(config => {
  config.headers.Authorization = `Bearer ${getToken()}`
  return config
})

// 统一响应处理
api.interceptors.response.use(
  response => response.data,
  error => {
    handleGlobalError(error)
    return Promise.reject(error)
  }
)

七、最佳实践

Fetch API 最佳实践封装

javascript
class HttpFetch {
  constructor(baseURL, defaultOptions = {}) {
    this.baseURL = baseURL
    this.defaultOptions = {
      headers: { 'Content-Type': 'application/json' },
      ...defaultOptions
    }
  }

  async request(url, options = {}) {
    const controller = new AbortController()
    const timeoutId = setTimeout(
      () => controller.abort(),
      options.timeout || 10000
    )

    try {
      const response = await fetch(`${this.baseURL}${url}`, {
        ...this.defaultOptions,
        ...options,
        signal: controller.signal
      })

      clearTimeout(timeoutId)

      if (!response.ok) {
        const error = await response.json()
        throw new Error(error.message || '请求失败')
      }

      return await response.json()
    } catch (error) {
      clearTimeout(timeoutId)
      if (error.name === 'AbortError') {
        throw new Error('请求超时')
      }
      throw error
    }
  }

  get(url, params, options) {
    const queryString = new URLSearchParams(params).toString()
    return this.request(`${url}?${queryString}`, { ...options, method: 'GET' })
  }

  post(url, data, options) {
    return this.request(url, {
      ...options,
      method: 'POST',
      body: JSON.stringify(data)
    })
  }
}

const http = new HttpFetch('https://api.example.com')

Axios 最佳实践封装

javascript
import axios from 'axios'

const createHttpClient = (config = {}) => {
  const instance = axios.create({
    timeout: 10000,
    ...config
  })

  // 请求拦截器
  instance.interceptors.request.use(
    config => {
      const token = localStorage.getItem('token')
      if (token) {
        config.headers.Authorization = `Bearer ${token}`
      }
      return config
    },
    error => Promise.reject(error)
  )

  // 响应拦截器
  instance.interceptors.response.use(
    response => response.data,
    error => {
      const { response } = error
      if (response) {
        switch (response.status) {
          case 401:
            localStorage.removeItem('token')
            window.location.href = '/login'
            break
          case 403:
            console.error('权限不足')
            break
          case 500:
            console.error('服务器错误')
            break
        }
      }
      return Promise.reject(error)
    }
  )

  return instance
}

export const http = createHttpClient({
  baseURL: process.env.VUE_APP_API_URL
})

八、总结

维度推荐选择原因
现代项目Axios 或 Fetch代码简洁,Promise 支持
企业级应用Axios功能完善,生态成熟
老旧浏览器XMLHttpRequest兼容性最好
微型项目Fetch零依赖,体积小
需要进度监控Axios 或 XMLHttpRequestFetch 不支持
流式处理Fetch原生支持流
学习成本FetchAPI 简单直观

最终建议

  • 新项目推荐 Axios,功能完善,开发效率高
  • 追求轻量级或现代特性,选择 Fetch
  • 需要兼容 IE 或有特殊需求,使用 XMLHttpRequest

基于 VitePress 的本地知识库