Vue基础教程(187)axios拦截器:通关秘籍:Vue中Axios拦截器,让你的代码从“能用”变身“牛逼”!

还记得刚学Vue的时候,每次发请求都要写一堆重复代码的日子吗?

// 曾经的你,是否这样写过?
this.loading = true
axios.get('/api/data')
  .then(response => {
    this.data = response.data
    this.loading = false
  })
  .catch(error => {
    this.loading = false
    alert('出错啦!')
    console.log(error)
  })

每个请求都要手动开关loading,每个错误都要单独处理... 累不累啊兄弟!

别担心,今天我要给你介绍的axios拦截器,就是来解救你于水火的!它就像给你的请求加了个“智能管家”,所有重复劳动统统自动化。

什么是拦截器?先来个通俗版解释

想象一下,你是个大老板,每天要收發很多快递(请求和响应)。拦截器就是你雇的两个贴心秘书:

  • 请求秘书:每次你寄快递前,她自动帮你包装好、贴上标签、记下快递单号
  • 响应秘书:每次收到快递,她先帮你拆箱检查,有问题直接处理,只把好东西交给你

这样一来,你只需要关心“要寄什么”和“收到后怎么用”,中间那些杂活秘书全包了!

实战开始:搭建你的拦截器帝国

第一步:基础装备

先确保你的项目里有axios:

npm install axios

然后创建一个专门的请求配置文件request.js

// src/utils/request.js
import axios from 'axios'

// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // 基础地址
  timeout: 10000 // 超时时间
})

// 这里就是我们今天的主角——拦截器!
第二步:请求拦截器 - 你的贴心小秘书

请求拦截器会在请求发出前执行,适合做这些事:

  • 自动添加token
  • 统一加loading
  • 参数序列化
// 请求拦截器
service.interceptors.request.use(
  config => {
    // 在发送请求前做些什么
    
    // 1. 自动添加token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    
    // 2. 显示loading(如果需要)
    if (config.showLoading !== false) {
      showLoading() // 你的loading显示方法
    }
    
    // 3. 记录请求开始时间(用于计算请求耗时)
    config.startTime = Date.now()
    
    console.log(`🚀 请求发出:${config.method?.toUpperCase()} ${config.url}`)
    return config
  },
  error => {
    // 对请求错误做些什么
    hideLoading() // 记得隐藏loading
    return Promise.reject(error)
  }
)

看到没?以后发请求自动带token,再也不用每个请求手动设置了!

第三步:响应拦截器 - 你的质检专员

响应拦截器会在收到响应后执行,适合:

  • 统一错误处理
  • 自动隐藏loading
  • 数据格式化
// 响应拦截器
service.interceptors.response.use(
  response => {
    // 2xx 范围内的状态码都会触发该函数
    
    // 1. 隐藏loading
    hideLoading()
    
    // 2. 计算请求耗时
    const costTime = Date.now() - response.config.startTime
    console.log(`✅ 请求成功:${response.config.url} (${costTime}ms)`)
    
    // 3. 直接返回数据部分,简化使用
    return response.data
  },
  error => {
    // 超出 2xx 范围的状态码都会触发该函数
    
    // 1. 隐藏loading
    hideLoading()
    
    // 2. 计算请求耗时
    const costTime = Date.now() - (error.config?.startTime || Date.now())
    console.log(`❌ 请求失败:${error.config?.url} (${costTime}ms)`)
    
    // 3. 统一错误处理
    handleError(error)
    
    return Promise.reject(error)
  }
)

// 统一错误处理函数
function handleError(error) {
  if (error.response) {
    // 服务器返回错误状态码
    const status = error.response.status
    const message = error.response.data?.message || '请求错误'
    
    switch (status) {
      case 401:
        // token过期,跳转到登录页
        alert('登录已过期,请重新登录')
        router.push('/login')
        break
      case 403:
        alert('没有权限访问')
        break
      case 404:
        alert('请求地址不存在')
        break
      case 500:
        alert('服务器开小差了,请稍后重试')
        break
      default:
        alert(message)
    }
  } else if (error.request) {
    // 请求发出去但没有收到响应
    alert('网络连接失败,请检查网络')
  } else {
    // 其他错误
    alert(error.message)
  }
}

第四步:完整实战代码 - 登录流程为例

来看一个完整的登录示例:

// src/utils/request.js
import axios from 'axios'
import { Toast } from 'vant' // 假设使用vant的提示组件

const service = axios.create({
  baseURL: 'https://api.your-app.com',
  timeout: 10000
})

let loadingCount = 0

function showLoading() {
  if (loadingCount === 0) {
    Toast.loading({
      message: '加载中...',
      forbidClick: true,
      duration: 0
    })
  }
  loadingCount++
}

function hideLoading() {
  loadingCount--
  if (loadingCount === 0) {
    Toast.clear()
  }
}

// 请求拦截器
service.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    
    if (config.showLoading !== false) {
      showLoading()
    }
    
    config.startTime = Date.now()
    return config
  },
  error => {
    if (error.config?.showLoading !== false) {
      hideLoading()
    }
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    if (response.config.showLoading !== false) {
      hideLoading()
    }
    
    const costTime = Date.now() - response.config.startTime
    console.log(`请求成功:${response.config.url} (${costTime}ms)`)
    
    // 根据你的后端返回结构调整
    const res = response.data
    if (res.code === 0 || res.success) {
      return res
    } else {
      // 业务逻辑错误
      Toast.fail(res.message || '请求失败')
      return Promise.reject(new Error(res.message || 'Error'))
    }
  },
  error => {
    if (error.config?.showLoading !== false) {
      hideLoading()
    }
    
    handleError(error)
    return Promise.reject(error)
  }
)

function handleError(error) {
  if (error.response) {
    const status = error.response.status
    const message = error.response.data?.message || '请求错误'
    
    switch (status) {
      case 401:
        Toast.fail('登录已过期')
        setTimeout(() => {
          localStorage.removeItem('token')
          window.location.href = '/login'
        }, 1500)
        break
      case 403:
        Toast.fail('没有权限')
        break
      default:
        Toast.fail(message)
    }
  } else if (error.request) {
    Toast.fail('网络连接失败')
  } else {
    Toast.fail(error.message)
  }
}

export default service

现在在组件中使用,简直爽到飞起:

// 在Vue组件中
import request from '@/utils/request'

export default {
  methods: {
    async login() {
      try {
        // 这么简洁!拦截器在背后默默处理了一切
        const result = await request.post('/user/login', {
          username: 'admin',
          password: '123456'
        })
        
        // 直接拿到业务数据
        if (result.success) {
          this.$router.push('/dashboard')
        }
      } catch (error) {
        // 错误已经被拦截器统一处理了,这里基本不用写东西
        console.log('登录失败:', error)
      }
    },
    
    async getData() {
      // 某个不需要loading的请求
      const data = await request.get('/api/data', {
        showLoading: false  // 个性化配置
      })
      this.data = data
    }
  }
}

高级玩法:让拦截器更智能

1. 请求重试机制
// 在响应拦截器中添加重试逻辑
service.interceptors.response.use(
  response => response,
  async error => {
    const config = error.config
    
    // 设置重试次数
    if (!config.retryCount) {
      config.retryCount = 3
    }
    
    if (config.retryCount > 0 && 
        (error.response?.status >= 500 || !error.response)) {
      config.retryCount--
      
      // 延迟重试
      await new Promise(resolve => 
        setTimeout(resolve, 1000 * (3 - config.retryCount))
      )
      
      return service(config)
    }
    
    return Promise.reject(error)
  }
)
2. 请求取消

在文件上传等场景特别有用:

// 创建取消令牌的Map
const pendingMap = new Map()

// 生成请求key
const getPendingKey = (config) => {
  return [config.method, config.url].join('&')
}

// 请求拦截器中记录
service.interceptors.request.use(config => {
  const key = getPendingKey(config)
  removePending(key) // 取消重复请求
  config.cancelToken = new axios.CancelToken(cancel => {
    pendingMap.set(key, cancel)
  })
  return config
})

// 响应拦截器中移除
service.interceptors.response.use(response => {
  const key = getPendingKey(response.config)
  removePending(key)
  return response
})

function removePending(key) {
  if (pendingMap.has(key)) {
    const cancel = pendingMap.get(key)
    cancel(key)
    pendingMap.delete(key)
  }
}

常见坑位提醒

  1. 拦截器执行顺序:后添加的拦截器先执行(类似栈)
  2. 异步问题:拦截器支持Promise,可以做异步操作
  3. 多个实例:不同axios实例有自己独立的拦截器
  4. 错误传递:一定要用Promise.reject传递错误,否则上层catch不到

总结

axios拦截器就像给你的项目请了个不要工资的全能助理:

  • ✅ 自动管理认证token
  • ✅ 统一loading状态
  • ✅ 全局错误处理
  • ✅ 请求日志记录
  • ✅ 性能监控
  • ✅ 数据格式化

从此告别重复代码,让你的Vue项目代码质量提升一个Level!以后再也不用在每个请求里写那些烦人的loading和错误处理了。

记住,好的工具要用在刀刃上。拦截器虽好,但也要根据实际项目需求来配置,别为了用而用。

赶紧去给你的项目配上这个神器吧,保证你用过之后就再也回不去了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值