Vue3单例Axios实例在事件处理器中调用时inject报错求助
问题描述
我编写了一个用于创建Axios实例的Vue插件,全局注册后,在组件setup阶段调用封装的API方法可正常使用该实例,但在按钮点击这类事件处理器中调用时,会报错:inject() can only be used inside setup() or functional components.。相关代码如下:
plugins/axios.ts
import axios from 'axios' import type { App } from 'vue' interface AxiosOptions { baseUrl: string } function isIsoDateString(value: any): boolean { ... } function handleDates(body: any) { ... } export default { install: (app: App, options: AxiosOptions) => { const axiosInstance = axios.create({ baseURL: options.baseUrl, headers: { "Accept": "application/json", "Content-type": "application/json" } }); axiosInstance.interceptors.response.use(originalResponse => { handleDates(originalResponse.data); return originalResponse; }); app.provide("axios", axiosInstance); } }
main.ts
import { createApp } from "vue"; import { createPinia } from "pinia"; import mitt from "mitt"; import axios from "@/plugins/axios"; import App from "./App.vue" import router from "./router"; const emitter = mitt(); const app = createApp(App) .use(router) .use(createPinia()) .use(axios, {baseUrl: "http://localhost:15454/"}); app.provide("emitter", emitter); app.mount("#app");
actions/providers.ts
import { inject } from 'vue' import type { AxiosInstance } from 'axios'; import { type Provider } from "../types/Provider"; export const getProviders = async (activeOnly: boolean): Promise<Provider[]> => { const ax = inject('axios') as AxiosInstance; try { const response = await ax.get(`/Provider/${activeOnly}`); return response.data; } catch (error) { ... return []; } };
报错场景代码
<script setup lang="ts"> const handleClick = async () => { const value = await getProviders(true); } </script> <template> <button @click="handleClick">Submit</button> </template>
报错原因
inject() 是Vue Composition API的方法,必须在组件的setup上下文执行期间调用。你的getProviders函数里直接调用了inject('axios'):
- 当在组件setup阶段调用
getProviders时,setup上下文还存在,inject能正常获取到提供的Axios实例; - 当在事件处理器(比如按钮点击)中调用时,setup已经执行完毕,此时调用
getProviders,inject找不到当前组件的上下文,就会抛出错误。
解决方案
下面提供三种可行的解决方法,按需选择:
方法一:在组件中注入实例后传给API函数
修改API函数,让它接收Axios实例作为参数,不再内部调用inject:
修改actions/providers.ts
import type { AxiosInstance } from 'axios'; import { type Provider } from "../types/Provider"; export const getProviders = async (axiosInstance: AxiosInstance, activeOnly: boolean): Promise<Provider[]> => { try { const response = await axiosInstance.get(`/Provider/${activeOnly}`); return response.data; } catch (error) { // 错误处理逻辑 return []; } };
修改组件代码
<script setup lang="ts"> import { inject } from 'vue' import type { AxiosInstance } from 'axios'; import { getProviders } from '@/actions/providers'; // 在setup阶段注入Axios实例 const axiosInstance = inject('axios') as AxiosInstance; const handleClick = async () => { // 调用时传入实例 const value = await getProviders(axiosInstance, true); } </script> <template> <button @click="handleClick">Submit</button> </template>
方法二:将Axios实例挂载到全局属性
修改插件,把Axios实例同时挂载到Vue应用的全局属性上,这样可以在组件中直接获取:
修改plugins/axios.ts的install方法
export default { install: (app: App, options: AxiosOptions) => { const axiosInstance = axios.create({ baseURL: options.baseUrl, headers: { "Accept": "application/json", "Content-type": "application/json" } }); axiosInstance.interceptors.response.use(originalResponse => { handleDates(originalResponse.data); return originalResponse; }); app.provide("axios", axiosInstance); // 添加这行,挂载到全局属性 app.config.globalProperties.$axios = axiosInstance; } }
修改组件代码
<script setup lang="ts"> import { getCurrentInstance } from 'vue' import { getProviders } from '@/actions/providers'; // 获取当前组件实例的代理 const { proxy } = getCurrentInstance()!; const handleClick = async () => { // 从全局属性获取实例并传入API函数 const value = await getProviders(proxy!.$axios, true); } </script>
方法三:直接导出Axios实例(推荐)
放弃provide/inject的方式,直接在模块中创建并导出Axios实例,这样API函数可以直接导入使用,不依赖组件上下文:
修改plugins/axios.ts
import axios from 'axios'; import type { AxiosInstance } from 'axios'; interface AxiosOptions { baseUrl: string } function isIsoDateString(value: any): boolean { ... } function handleDates(body: any) { ... } // 声明实例变量 export let axiosInstance: AxiosInstance; // 初始化方法 export const initAxios = (options: AxiosOptions) => { axiosInstance = axios.create({ baseURL: options.baseUrl, headers: { "Accept": "application/json", "Content-type": "application/json" } }); axiosInstance.interceptors.response.use(originalResponse => { handleDates(originalResponse.data); return originalResponse; }); }
修改main.ts
import { createApp } from "vue"; import { createPinia } from "pinia"; import mitt from "mitt"; import { initAxios } from "@/plugins/axios"; import App from "./App.vue" import router from "./router"; const emitter = mitt(); // 先初始化Axios实例 initAxios({baseUrl: "http://localhost:15454/"}); const app = createApp(App) .use(router) .use(createPinia()); app.provide("emitter", emitter); app.mount("#app");
修改actions/providers.ts
import { axiosInstance } from '@/plugins/axios'; import { type Provider } from "../types/Provider"; export const getProviders = async (activeOnly: boolean): Promise<Provider[]> => { try { const response = await axiosInstance.get(`/Provider/${activeOnly}`); return response.data; } catch (error) { // 错误处理逻辑 return []; } };
组件代码无需修改
此时不管在setup还是事件处理器中调用getProviders,都能正常使用Axios实例,无需额外处理。
内容的提问来源于stack exchange,提问作者Leigh.D
相关产品推荐
相关产品推荐

