详细说一下Axios的用法,非常干货!

1. 完整的实例配置

// http.js
import axios from 'axios'

const instance = axios.create({
  // 基础URL,会自动加在请求URL前面
  baseURL: process.env.VUE_APP_API_URL,
  
  // 请求超时时间(毫秒)
  timeout: 5000,
  
  // 自定义请求头
  headers: {
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest',
    'platform': 'web'
  },
  
  // 是否携带cookie信息
  withCredentials: true,
  
  // 请求体编码方式
  responseType: 'json',
  
  // 用于限制请求内容的大小
  maxContentLength: 2000,
  
  // 用于限制上传内容的大小
  maxBodyLength: 2000,
  
  // 自定义验证状态码的范围
  validateStatus: function (status) {
    return status >= 200 && status < 300
  }
})

2. 详细的拦截器配置

// interceptors.js

/**
 * 请求拦截器配置
 * @param {Object} config - 请求配置对象
 * @returns {Object} 处理后的请求配置
 */
instance.interceptors.request.use(
  config => {
    // 1. 添加 loading 效果
    startLoading()
    
    // 2. 添加token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    
    // 3. GET请求添加时间戳防止缓存
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        _t: Date.now()
      }
    }
    
    // 4. POST请求数据处理
    if (config.method === 'post') {
      // 4.1 数据转换
      if (config.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
        config.data = qs.stringify(config.data)
      }
      
      // 4.2 数据加密(如果需要)
      if (config.needEncrypt) {
        config.data = encrypt(config.data)
      }
    }
    
    // 5. 添加自定义请求头
    config.headers['X-Custom-Header'] = 'custom-value'
    
    return config
  },
  error => {
    stopLoading()
    return Promise.reject(error)
  }
)

/**
 * 响应拦截器配置
 * @param {Object} response - 响应对象
 * @returns {Object} 处理后的响应数据
 */
instance.interceptors.response.use(
  response => {
    // 1. 关闭 loading 效果
    stopLoading()
    
    // 2. 获取响应数据
    const { code, data, message } = response.data
    
    // 3. 处理不同状态码
    switch (code) {
      case 200:
        return data
      case 401:
        handleUnauthorized()
        break
      case 403:
        handleForbidden()
        break
      default:
        handleError(message)
    }
    
    return Promise.reject(new Error(message))
  },
  error => {
    stopLoading()
    
    // 1. 处理请求超时
    if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
      handleTimeout()
      return Promise.reject(new Error('请求超时'))
    }
    
    // 2. 处理网络错误
    if (!error.response) {
      handleNetworkError()
      return Promise.reject(new Error('网络错误'))
    }
    
    // 3. 处理HTTP错误
    const { status } = error.response
    handleHttpError(status)
    
    return Promise.reject(error)
  }
)

3. 完整的错误处理模块

// errorHandler.js

/**
 * 统一的错误处理模块
 */
export const ErrorHandler = {
  // 处理未授权错误
  handleUnauthorized() {
    // 1. 清除本地token
    localStorage.removeItem('token')
    
    // 2. 显示错误消息
    Message.error('登录已过期,请重新登录')
    
    // 3. 跳转登录页
    router.push({
      path: '/login',
      query: {
        redirect: router.currentRoute.fullPath
      }
    })
  },
  
  // 处理权限不足
  handleForbidden() {
    Message.error('您没有权限访问该资源')
    router.push('/403')
  },
  
  // 处理请求超时
  handleTimeout() {
    Message.error('请求超时,请稍后重试')
  },
  
  // 处理网络错误
  handleNetworkError() {
    Message.error('网络连接失败,请检查网络设置')
  },
  
  // 处理HTTP错误
  handleHttpError(status) {
    const errorMap = {
      400: '请求参数错误',
      401: '未授权,请登录',
      403: '拒绝访问',
      404: '请求地址出错',
      408: '请求超时',
      500: '服务器内部错误',
      501: '服务未实现',
      502: '网关错误',
      503: '服务不可用',
      504: '网关超时',
      505: 'HTTP版本不受支持'
    }
    Message.error(errorMap[status] || `未知错误:${status}`)
  }
}

4. 请求方法封装

// request.js

/**
 * 请求方法封装
 * @param {string} url - 请求地址
 * @param {Object} params - 请求参数
 * @param {Object} options - 配置选项
 */
export const request = {
  /**
   * GET请求
   * @param {string} url - 请求地址
   * @param {Object} params - URL参数
   * @param {Object} options - 配置选项
   */
  async get(url, params = {}, options = {}) {
    try {
      const config = {
        ...options,
        params: {
          ...params,
          _t: Date.now() // 防止缓存
        }
      }
      return await instance.get(url, config)
    } catch (error) {
      handleRequestError(error)
      throw error
    }
  },
  
  /**
   * POST请求
   * @param {string} url - 请求地址
   * @param {Object} data - 请求体数据
   * @param {Object} options - 配置选项
   */
  async post(url, data = {}, options = {}) {
    try {
      const config = {
        ...options,
        headers: {
          'Content-Type': 'application/json',
          ...options.headers
        }
      }
      return await instance.post(url, data, config)
    } catch (error) {
      handleRequestError(error)
      throw error
    }
  },
  
  /**
   * 文件上传
   * @param {string} url - 上传地址
   * @param {File} file - 文件对象
   * @param {Object} options - 配置选项
   */
  async upload(url, file, options = {}) {
    const formData = new FormData()
    formData.append('file', file)
    
    try {
      const config = {
        ...options,
        headers: {
          'Content-Type': 'multipart/form-data',
          ...options.headers
        },
        onUploadProgress: progressEvent => {
          const complete = (progressEvent.loaded / progressEvent.total * 100 | 0)
          options.onProgress?.(complete)
        }
      }
      return await instance.post(url, formData, config)
    } catch (error) {
      handleRequestError(error)
      throw error
    }
  },
  
  /**
   * 文件下载
   * @param {string} url - 下载地址
   * @param {string} filename - 文件名
   * @param {Object} options - 配置选项
   */
  async download(url, filename, options = {}) {
    try {
      const config = {
        ...options,
        responseType: 'blob',
        onDownloadProgress: progressEvent => {
          const complete = (progressEvent.loaded / progressEvent.total * 100 | 0)
          options.onProgress?.(complete)
        }
      }
      
      const response = await instance.get(url, config)
      const blob = new Blob([response.data])
      const downloadUrl = window.URL.createObjectURL(blob)
      
      const link = document.createElement('a')
      link.href = downloadUrl
      link.download = filename
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      window.URL.revokeObjectURL(downloadUrl)
      
      return response
    } catch (error) {
      handleRequestError(error)
      throw error
    }
  }
}

5. API模块封装示例

// api/user.js

/**
 * 用户相关API
 */
export const userApi = {
  /**
   * 获取用户列表
   * @param {Object} params - 查询参数
   * @param {number} params.page - 页码
   * @param {number} params.size - 每页数量
   * @returns {Promise} 用户列表数据
   */
  getUsers(params) {
    return request.get('/users', params)
  },
  
  /**
   * 创建用户
   * @param {Object} data - 用户数据
   * @returns {Promise} 创建结果
   */
  createUser(data) {
    return request.post('/users', data)
  },
  
  /**
   * 更新用户信息
   * @param {string|number} id - 用户ID
   * @param {Object} data - 更新数据
   * @returns {Promise} 更新结果
   */
  updateUser(id, data) {
    return request.put(`/users/${id}`, data)
  },
  
  /**
   * 删除用户
   * @param {string|number} id - 用户ID
   * @returns {Promise} 删除结果
   */
  deleteUser(id) {
    return request.delete(`/users/${id}`)
  }
}

6. 使用示例

// 组件中使用
import { userApi } from '@/api/user'

export default {
  data() {
    return {
      users: [],
      loading: false,
      query: {
        page: 1,
        size: 10
      }
    }
  },
  
  methods: {
    // 获取用户列表
    async fetchUsers() {
      try {
        this.loading = true
        const data = await userApi.getUsers(this.query)
        this.users = data.list
        this.total = data.total
      } catch (error) {
        console.error('获取用户列表失败:', error)
      } finally {
        this.loading = false
      }
    },
    
    // 创建用户
    async createUser(userData) {
      try {
        await userApi.createUser(userData)
        this.$message.success('创建成功')
        this.fetchUsers()
      } catch (error) {
        this.$message.error('创建失败')
      }
    }
  }
}

这个完整的 Axios 配置和使用方案包含了:

  • 详细的实例配置
  • 完整的拦截器处理
  • 统一的错误处理
  • 规范的请求方法封装
  •  标准的 API 模块组织
  • 实际的使用示例

每个部分都有详细的注释说明,便于理解和使用。这种方案可以作为一个完整的项目请求层解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值