MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue项目中使用拦截器处理网络请求与响应

2022-05-117.7k 阅读

一、Vue 项目中的网络请求概述

在 Vue 项目开发中,与后端服务器进行数据交互是极为常见的需求。无论是获取用户信息、加载列表数据,还是提交表单、更新数据等操作,都离不开网络请求。目前,在 Vue 项目中最常用的网络请求库之一是 Axios。Axios 是一个基于 Promise 的 HTTP 客户端,它既可以在浏览器中使用,也可以在 Node.js 环境中使用。它具有简洁的 API、支持拦截器、自动转换 JSON 数据等诸多优点,这使得它成为 Vue 项目网络请求的首选库。

(一)Axios 的基本使用

在 Vue 项目中使用 Axios 首先需要安装它。可以通过 npm 或 yarn 进行安装:

npm install axios --save
# 或者
yarn add axios

安装完成后,在 main.js 文件中引入并配置 Axios:

import Vue from 'vue'
import axios from 'axios'

Vue.prototype.$axios = axios

这样在 Vue 组件中就可以通过 this.$axios 来发起网络请求了。例如,发送一个 GET 请求获取数据:

export default {
  name: 'ExampleComponent',
  mounted() {
    this.$axios.get('/api/data')
    .then(response => {
        console.log(response.data)
      })
    .catch(error => {
        console.error('请求失败', error)
      })
  }
}

同样,发送 POST 请求:

export default {
  name: 'ExampleComponent',
  data() {
    return {
      formData: {
        username: 'test',
        password: '123456'
      }
    }
  },
  methods: {
    submitForm() {
      this.$axios.post('/api/login', this.formData)
      .then(response => {
          console.log(response.data)
        })
      .catch(error => {
          console.error('请求失败', error)
        })
    }
  }
}

二、拦截器的概念与作用

(一)拦截器是什么

拦截器(Interceptor)是 Axios 提供的一个强大功能,它允许我们在请求或响应被 then 或 catch 处理前拦截它们。可以把拦截器想象成一个“关卡”,在请求发出之前或响应返回之后,我们可以在这个“关卡”中执行一些通用的逻辑。

(二)拦截器的作用

  1. 请求拦截器的作用
    • 添加通用请求头:在大多数项目中,可能需要在每个请求中添加一些通用的请求头,比如认证令牌(token)。通过请求拦截器,我们可以方便地在每个请求发出前自动添加这些请求头,而不需要在每个单独的请求中手动添加。
    • 请求参数处理:有时候需要对请求参数进行统一的处理,比如对参数进行加密、格式化等操作。请求拦截器提供了一个集中处理这些逻辑的地方,避免在每个请求处重复编写相同的代码。
  2. 响应拦截器的作用
    • 全局错误处理:当后端返回的响应状态码不是 2xx 时,意味着发生了错误。通过响应拦截器,我们可以统一捕获这些错误,并进行相应的处理,比如弹出错误提示框,告知用户发生了什么问题。
    • 数据处理与格式化:后端返回的数据格式可能并不完全符合前端的使用需求,例如时间格式可能需要转换、数据结构可能需要调整等。响应拦截器可以对返回的数据进行统一的处理和格式化,使前端使用数据更加方便。

三、Vue 项目中使用请求拦截器

(一)添加请求拦截器

在 Vue 项目中,通常在 main.js 文件中添加请求拦截器。继续上面配置 Axios 的步骤,添加请求拦截器如下:

import Vue from 'vue'
import axios from 'axios'

Vue.prototype.$axios = axios

// 添加请求拦截器
axios.interceptors.request.use(config => {
  // 在发送请求之前做些什么
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, error => {
  // 对请求错误做些什么
  return Promise.reject(error)
})

在上述代码中,axios.interceptors.request.use 方法用于添加请求拦截器。它接收两个回调函数作为参数,第一个回调函数在请求发送前被调用,第二个回调函数用于处理请求错误。在第一个回调函数中,我们首先从本地存储中获取 token,如果存在 token,则在请求头中添加 Authorization 字段,值为 Bearer 加上 token。这样,每个请求都会带上这个认证信息。

(二)请求参数处理

假设后端要求请求参数中的日期格式统一为 YYYY - MM - DD 格式,而前端组件中传递的日期可能是 JavaScript 的 Date 对象。我们可以在请求拦截器中对日期参数进行格式化处理。例如:

axios.interceptors.request.use(config => {
  const data = config.data
  if (data) {
    Object.keys(data).forEach(key => {
      if (data[key] instanceof Date) {
        data[key] = data[key].toISOString().split('T')[0]
      }
    })
  }
  return config
}, error => {
  return Promise.reject(error)
})

在这段代码中,我们遍历请求数据中的每个键值对,如果值是 Date 对象,则将其格式化为 YYYY - MM - DD 格式。这样就统一处理了请求参数中的日期格式问题。

四、Vue 项目中使用响应拦截器

(一)添加响应拦截器

同样在 main.js 文件中添加响应拦截器:

// 添加响应拦截器
axios.interceptors.response.use(response => {
  // 对响应数据做点什么
  return response.data
}, error => {
  // 对响应错误做点什么
  const status = error.response.status
  if (status === 401) {
    // 处理未授权错误,例如跳转到登录页面
    router.push('/login')
  } else if (status === 404) {
    // 处理资源未找到错误
    Vue.prototype.$message.error('资源未找到')
  }
  return Promise.reject(error)
})

axios.interceptors.response.use 方法用于添加响应拦截器。第一个回调函数在响应成功时被调用,这里我们直接返回 response.data,因为通常我们只关心后端返回的数据部分。第二个回调函数在响应错误时被调用,我们根据不同的状态码进行不同的处理。例如,当状态码为 401 时,跳转到登录页面,当状态码为 404 时,弹出“资源未找到”的错误提示。

(二)数据处理与格式化

假设后端返回的时间格式是时间戳,而前端需要显示为 YYYY - MM - DD HH:MM:SS 格式。我们可以在响应拦截器中进行处理:

axios.interceptors.response.use(response => {
  const data = response.data
  if (data) {
    if (data.createTime) {
      const date = new Date(data.createTime)
      data.createTime = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds()
    }
  }
  return response.data
}, error => {
  return Promise.reject(error)
})

在上述代码中,我们检查返回数据中的 createTime 字段,如果存在,则将时间戳转换为指定的日期时间格式。这样前端在使用数据时,就可以直接显示格式化后的时间。

五、在 Vuex 中结合拦截器使用

(一)Vuex 简介

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。在一个大型 Vue 项目中,使用 Vuex 可以更好地管理应用的状态,提高代码的可维护性和可扩展性。

(二)在 Vuex 中使用拦截器

  1. 处理登录状态 假设我们有一个登录模块,登录成功后,后端会返回一个 token。我们可以在响应拦截器中处理这个 token,并将其存储到 Vuex 中。
axios.interceptors.response.use(response => {
  if (response.config.url === '/api/login' && response.data.token) {
    store.commit('SET_TOKEN', response.data.token)
  }
  return response.data
}, error => {
  return Promise.reject(error)
})

在这段代码中,当请求的 URL 是 /api/login 且响应数据中包含 token 时,我们通过 store.commit 方法调用 Vuex 中的 SET_TOKEN mutation,将 token 存储到 Vuex 的状态中。 2. 全局状态更新 有时候,后端返回的响应可能会影响到全局的状态。例如,当用户信息更新后,后端返回最新的用户信息。我们可以在响应拦截器中处理这个响应,并更新 Vuex 中的用户信息状态。

axios.interceptors.response.use(response => {
  if (response.config.url === '/api/user/update' && response.data.user) {
    store.commit('UPDATE_USER_INFO', response.data.user)
  }
  return response.data
}, error => {
  return Promise.reject(error)
})

这里当请求的 URL 是 /api/user/update 且响应数据中包含 user 信息时,我们调用 UPDATE_USER_INFO mutation 更新 Vuex 中的用户信息状态。

六、拦截器的注意事项

(一)拦截器的顺序

如果添加了多个请求拦截器或响应拦截器,它们的执行顺序是按照添加的顺序执行的。例如,先添加的请求拦截器会先执行,先添加的响应拦截器会后执行(因为响应拦截器是在响应返回时执行,后添加的更靠近响应的处理逻辑)。在编写拦截器时,要注意逻辑的先后顺序,避免出现逻辑错误。

(二)错误处理

在拦截器的错误处理回调函数中,一定要返回 Promise.reject(error),这样才能保证错误能够被正确传递到后续的 catch 块中。如果不返回 Promise.reject(error),错误可能会被忽略,导致问题难以排查。例如:

axios.interceptors.request.use(config => {
  // 正常处理
  return config
}, error => {
  // 错误处理
  console.error('请求错误', error)
  // 一定要返回 Promise.reject(error)
  return Promise.reject(error)
})

(三)避免循环调用

在拦截器中要避免出现循环调用的情况。例如,在请求拦截器中修改了请求的 URL,而这个新的 URL 又触发了请求拦截器,就可能导致无限循环。在编写拦截器逻辑时,要仔细考虑可能出现的各种情况,避免这种问题的发生。

七、实际项目中的应用场景案例

(一)多环境配置与请求前缀

在实际项目开发中,通常会有开发环境、测试环境和生产环境。不同环境的 API 地址可能不同。我们可以通过在请求拦截器中动态设置请求前缀来解决这个问题。

  1. 配置文件 首先,在项目根目录下创建一个 config 文件夹,并在其中创建一个 env.js 文件用于存储不同环境的配置:
const env = process.env.NODE_ENV
let baseUrl = ''
if (env === 'development') {
  baseUrl = 'http://dev.api.com'
} else if (env === 'test') {
  baseUrl = 'http://test.api.com'
} else if (env === 'production') {
  baseUrl = 'http://api.com'
}
export default {
  baseUrl
}
  1. 请求拦截器配置 然后在 main.js 文件中,结合上述配置在请求拦截器中设置请求前缀:
import env from './config/env'
axios.interceptors.request.use(config => {
  config.url = env.baseUrl + config.url
  return config
}, error => {
  return Promise.reject(error)
})

这样,在不同环境下,请求会自动加上对应的 API 前缀,方便进行多环境开发与部署。

(二)加载动画处理

在网络请求过程中,为了提升用户体验,通常会显示一个加载动画。我们可以利用拦截器来实现这一功能。

  1. 定义加载状态 在 Vuex 中定义一个加载状态的状态变量,例如:
const state = {
  isLoading: false
}
const mutations = {
  SET_LOADING(state, status) {
    state.isLoading = status
  }
}
  1. 拦截器实现加载动画 在请求拦截器中设置加载状态为 true,在响应拦截器中设置加载状态为 false
axios.interceptors.request.use(config => {
  store.commit('SET_LOADING', true)
  return config
}, error => {
  store.commit('SET_LOADING', false)
  return Promise.reject(error)
})
axios.interceptors.response.use(response => {
  store.commit('SET_LOADING', false)
  return response.data
}, error => {
  store.commit('SET_LOADING', false)
  return Promise.reject(error)
})

在 Vue 组件中,可以根据 isLoading 状态来显示或隐藏加载动画:

<template>
  <div>
    <loading v-if="isLoading"></loading>
    <!-- 其他组件内容 -->
  </div>
</template>
<script>
export default {
  computed: {
    isLoading() {
      return this.$store.state.isLoading
    }
  }
}
</script>

(三)数据缓存

在一些情况下,我们希望对某些网络请求的数据进行缓存,以减少不必要的网络请求。可以在响应拦截器中实现简单的数据缓存。

  1. 定义缓存对象 在 main.js 文件中定义一个缓存对象:
const cache = {}
  1. 响应拦截器实现缓存
axios.interceptors.response.use(response => {
  const url = response.config.url
  if (!cache[url]) {
    cache[url] = response.data
  }
  return response.data
}, error => {
  return Promise.reject(error)
})
  1. 请求拦截器读取缓存 在请求拦截器中判断是否有缓存数据,如果有则直接返回缓存数据,不再发起网络请求:
axios.interceptors.request.use(config => {
  const url = config.url
  if (cache[url]) {
    return Promise.resolve({ data: cache[url] })
  }
  return config
}, error => {
  return Promise.reject(error)
})

这样,对于相同 URL 的请求,如果有缓存数据,就会直接使用缓存数据,提高了应用的性能。

八、总结拦截器在 Vue 项目中的优势与拓展

(一)拦截器的优势

  1. 代码复用:通过拦截器,我们可以将一些通用的逻辑,如添加请求头、错误处理等,集中在拦截器中处理,避免在每个请求或响应处理处重复编写相同的代码,提高了代码的复用性。
  2. 易于维护:由于通用逻辑都集中在拦截器中,当需要修改这些逻辑时,只需要在拦截器中进行修改,而不需要在大量的请求和响应处理代码中逐个查找和修改,降低了维护成本。
  3. 提升用户体验:利用拦截器可以方便地实现加载动画、错误提示等功能,从而提升用户在使用应用时的体验。

(二)拦截器的拓展

  1. 自定义拦截器:除了 Axios 自带的请求和响应拦截器,我们还可以自定义拦截器。例如,创建一个专门用于处理特定业务逻辑的拦截器,在其中进行特定的数据验证或处理。
  2. 结合其他库使用:可以将拦截器与其他库结合使用,例如结合 Vue Router 实现根据响应状态进行页面导航,或者结合 Vuex 和 localStorage 实现更复杂的状态管理和数据缓存策略。

在 Vue 项目中,合理使用拦截器能够极大地提高开发效率、优化代码结构,并提升用户体验。通过深入理解和灵活运用拦截器,开发者可以更好地掌控网络请求与响应的处理过程,构建出更加健壮和高效的前端应用。