在 Nuxt3 使用 Axios 进行开发的时候遇到了跨域问题,查了 Nuxt3 的官方文档后得知需要使用 nitro
代理向后端请求数据,需要修改 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
文件内容如下:
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
进行二次封装的话,欢迎留言赐教,谢谢。