在 Nuxt3 使用 Axios 进行开发的时候遇到了跨域问题,查了 Nuxt3 的官方文档后得知需要使用 nitro 代理向后端请求数据,需要修改 nuxt.config.ts 文件,修改后的配置为:

nuxt.config.ts
export default defineNuxtConfig({
  pages: true,
  css: ["~/assets/css/main.css"],
  postcss: {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  },
  modules: ["nuxt-icon", "@pinia/nuxt", "@pinia-plugin-persistedstate/nuxt"],
  // devtools: { enabled: true },
  
  // 跨域请求数据
  nitro: {
    devProxy: {
      "/api/v1": {
        target: "http://localhost:44044/api/v1",
        changeOrigin: true,
      },
    },
  },
});

在我的 Nuxt3 中是将 Axios 配置成了插件的形式使用,安装 Axios: pnpm add axios ,安装安毕后在项目根目录的 plugins 文件夹中新建 axios.plugin.js 文件,这里我使用的是 JS 没有使用 TS(个人感觉在自己的小项目上使用 TS 会有多余类型负担), axios.plugin.js 文件内容如下:

plugin/axios.plugin.js
import axios from 'axios'
// 引入自定义store
import { useManagerStore } from '~/stores/managerGeneral.store'
export default defineNuxtPlugin((NuxtApp) => {
  const instance = axios.create({
    baseURL: '/api/v1',
    withCredentials: true,
    timeout: 3000
  })
// 添加请求拦截器
instance.interceptors.request.use(
  function (config) {
    // 在发送请求之前做些什么
    if (useManagerStore().token) {
      config.headers.Authorization = `Bearer ${useManagerStore().token}`;
    }
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);
// 添加响应拦截器
instance.interceptors.response.use(
  function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    if (response.status.toString().startsWith("2")) {
      return response.data;
    }
    return response;
  },
  async function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    let originalRequest = error.config;
    if (error.response.data.error.statusCode === 401 && !originalRequest._retry && useManagerStore().token) { 
      originalRequest._retry = true;
      try {
        let result = await instance.post('/auth/refreshToken', { token: useManagerStore().token })
        useManagerStore().token = result.access_token;
        originalRequest.headers.Authorization = `Bearer ${access_token}`;
        return instance(originalRequest);
      } catch (error) {
        // useManagerStore().token = null
        return Promise.reject(error);
      }
    }
    return Promise.reject(error.response.data.error);
  }
);
  return {
    provide: {
      axios: instance
    }
  }
})

在上面的代码中,我对 axios 进行了简单的二次封装,在 axios 初始化时添加了根路径 /api/v1 这是因为我后端的路径中的地址后面是添加了 /api/v1 两个路径的,如果你的后端路径中没有可以直接修改为 /

还添加了请求拦截器和响应拦截器:

  • 请求拦截器中每次请求前会添加认证的 token 信息;

  • 响应拦截器中添加了状态码识别,对 2 开头和 4 开头的状态码进行分别处理;

在请求时添加的认证 token 是在自定义的 store 中获取的,我是使用 Pinia 进行状态管理,要在 Nuxt3 中使用 Pinia 的话我也是将它封装成了一个插件,具体如何操作之后再做演示。

在需要向后端请求数据的页面中需要在 useNuxtApp() 方法中解构出插件 $axios

<script setup>
const { $axios } = useNuxtApp()
let data = ref(null)
onMounted(async () => {
  data.value = await $axios.get('/')
})
<script>
  
<template>
  {{ data }}
<template>

这样使用 axios 优点是代码简单、直观,缺点是不方便,在每个需要请求的页面中都需要解构 $axios 插件。

其实在 Vue3 中有更好的写法,但我在 Nuxt3 中没有成功实现,应该是因为 Nuxt3 是约定大于配置的原因吧,在 Nuxt3 官方文档中是推荐使用自带的 useFetch 的,但我使用下来觉得太不方便,如果有技术大佬知道如何对 useFetch 进行二次封装的话,欢迎留言赐教,谢谢。

阅读次数 428