如何让第二个Pinia插件等待第一个插件执行完成?
解决方案
方法一:合并插件,强制顺序执行
Pinia 默认不会等待异步插件执行完成再加载下一个,可将两个插件包装成一个异步插件,先完成认证逻辑,再初始化 API:
// 创建合并插件 import { authenticationPlugin } from './authentication'; import { apiPlugin } from './api'; import type { PiniaPluginContext } from 'pinia'; export async function combinedPlugin(context: PiniaPluginContext) { // 等待认证逻辑执行完成 await authenticationPlugin(context); // 再执行API初始化 await apiPlugin(context); }
在注册 Pinia 时使用该合并插件:
// main.ts import { createPinia } from 'pinia'; import { combinedPlugin } from './plugins/combined'; const pinia = createPinia(); pinia.use(combinedPlugin);
此方式确保认证插件的异步操作完成后,API 插件才启动,初始化 API 实例时 store.idToken 已存在有效值。
方法二:修改API插件,延迟初始化API实例
保留两个独立插件,调整 API 插件逻辑,仅在 idToken 存在时创建 API 实例,同时订阅 idToken 变化更新 API:
// 修改后的apiPlugin.ts export async function apiPlugin({ store }: PiniaPluginContext) { const msalInstance = store.msalInstance as PublicClientApplication; // 封装API实例创建逻辑 const createApi = (token: string) => { const api = new Api(new Configuration({ basePath: config.BASE_PATH, accessToken: token })); // 调整拦截器逻辑,自动处理token过期 api.axios.interceptors.request.use(async (config) => { const tokenResponse = await msalInstance.acquireTokenSilent(store.msalRequest); if (tokenResponse.expiresOn!.getTime() < Date.now()) { const { idToken } = await msalInstance.acquireTokenSilent(store.msalRequest); store.idToken = idToken; // 更新store中的API实例 store.api = createApi(idToken); config.headers.Authorization = `Bearer ${idToken}`; } else { config.headers.Authorization = `Bearer ${tokenResponse.idToken}`; } return config; }); return api; }; // 订阅idToken变化,更新API实例 store.$subscribe((mutation) => { if (mutation.events.key === 'idToken' && mutation.events.newValue) { store.api = createApi(mutation.events.newValue); } }); // 初始化时若idToken已存在,直接创建API if (store.idToken) { store.api = createApi(store.idToken); } }
调用 API 的组件中,需确保 API 实例就绪后再执行请求:
<script setup> import { useStore } from '@/stores/yourStore'; import { watch } from 'vue'; const store = useStore(); // 监听API实例状态,就绪后调用接口 watch(() => store.api, (api) => { if (api) { api.yourApiMethod().then(res => { // 处理响应数据 }); } }, { immediate: true }); </script>
方法三:在Store内部处理初始化逻辑
放弃使用插件,直接在 Pinia Store 的 action 中处理认证和 API 初始化,通过控制执行顺序保证依赖关系:
// stores/main.ts import { defineStore } from 'pinia'; import { PublicClientApplication, InteractionRequiredAuthError } from '@azure/msal-browser'; import { Api, Configuration } from '@/api'; import config from '@/api/config'; export const useMainStore = defineStore('main', { state: () => ({ msalInstance: new PublicClientApplication({ // 你的MSAL配置参数 }), msalRequest: { // 你的MSAL请求配置 }, idToken: null as string | null, api: null as Api | null, }), actions: { async initAuth() { try { const { idToken } = await this.msalInstance.ssoSilent(this.msalRequest); const account = this.msalInstance.getAllAccounts()[0]; this.msalInstance.setActiveAccount(account); this.idToken = idToken; } catch (error) { if (error instanceof InteractionRequiredAuthError) { const { idToken } = await this.msalInstance.acquireTokenPopup(this.msalRequest); this.idToken = idToken; } } }, initApi() { if (!this.idToken) return; this.api = new Api(new Configuration({ basePath: config.BASE_PATH, accessToken: this.idToken, })); this.api.axios.interceptors.request.use(async (config) => { const tokenResponse = await this.msalInstance.acquireTokenSilent(this.msalRequest); if (tokenResponse.expiresOn!.getTime() < Date.now()) { const { idToken } = await this.msalInstance.acquireTokenSilent(this.msalRequest); this.idToken = idToken; this.initApi(); config.headers.Authorization = `Bearer ${idToken}`; } else { config.headers.Authorization = `Bearer ${tokenResponse.idToken}`; } return config; }); } } });
应用启动时,先完成认证再初始化 API:
// main.ts import { createApp } from 'vue'; import { useMainStore } from './stores/main'; import App from './App.vue'; async function bootstrap() { const app = createApp(App); const store = useMainStore(); // 先执行认证逻辑 await store.initAuth(); // 再初始化API实例 store.initApi(); app.mount('#app'); } bootstrap();
此方式确保应用挂载前已完成认证和 API 初始化,组件渲染时可直接使用带有效 token 的 API 实例。
内容的提问来源于stack exchange,提问作者Ricardo de Vries
相关产品推荐
相关产品推荐

