Vue+Pinia授权:函数返回Promise而非值导致v-if判断异常
Vue + Pinia 权限校验返回Promise导致v-if判断异常
问题描述
使用Vue和Pinia实现前端授权功能,目标是获取当前用户权限列表,在组件中判断是否有权限查看特定内容。但通过v-if调用方法或计算属性校验权限时,函数始终返回Promise而非预期的布尔值,导致v-if默认判定为true,错误显示无权限的元素。
现有代码
Pinia Store(auth.js)
export const useAuthStore = defineStore({ id: "authentication", state: () => ({ // 其他状态 userPermissions: fetchUserPerm(), // 直接赋值异步函数返回的Promise }), }); const fetchUserPerm = async () => { const res = await axios.get("api/users/me"); // Laravel后端返回包含Role属性的User对象,Role中存储用户权限列表 return res.data.role.permissions; };
组件代码
<script> import router from "../../../router"; import axios from "axios"; import Modal from "../../UI/Modal.vue"; import { useAuthStore } from "../../../stores/auth"; export default { setup() { const authStore = useAuthStore(); return { authStore }; }, data() { return { // 其他数据 userPermissions: this.authStore.userPermissions, filteredPermissions: null, }; }, methods:{ async checkUserPermFn(value) { this.userPermissions = await this.userPermissions; this.filteredPermissions = this.userPermissions.filter((permission) => { return permission.name.includes(value); }); console.log(this.filteredPermissions); // 能正确打印过滤后的空数组 return this.filteredPermissions.length > 0; // 返回值仍是Promise }, }, computed:{ async checkPermission() { this.userPermissions = await this.userPermissions; console.log(this.userPermissions,"computed"); // 能正确打印权限数组 this.filteredPermissions = this.userPermissions.filter( (permission) => { return permission.name.includes('permission.name'); } ); console.log(this.filteredPermissions,"computed"); // 能正确打印过滤后的空数组 console.log(this.filteredPermissions.length) // 正确打印0 return this.filteredPermissions.length > 0; // 返回值仍是Promise }, }, }; </script> <!-- 模板使用示例 --> <div v-if="checkUserPermFn(value)"><p>Hi!</p></div> <!-- 或使用异步计算属性 --> <div v-if="checkPermission"><p>Hi!</p></div>
核心问题
- Pinia State存储了Promise:直接将异步函数
fetchUserPerm()赋值给userPermissions,导致state中存储的是Promise而非最终权限数组。 - Vue无法处理
v-if中的Promise:任何异步方法/计算属性返回的Promise在v-if中都会被视为truthy值,因此即使最终fulfilled值为false,元素仍会被显示。
解决方案
步骤1:修复Pinia Store,用Action处理异步请求
将异步获取权限的逻辑移到Pinia的Action中,初始化时userPermissions设为空数组:
export const useAuthStore = defineStore({ id: "authentication", state: () => ({ // 其他状态 userPermissions: [], // 初始化为空数组 isLoading: false, // 可选:添加加载状态,避免组件渲染时权限未加载完成 }), actions: { async fetchUserPerm() { this.isLoading = true; try { const res = await axios.get("api/users/me"); this.userPermissions = res.data.role.permissions; } catch (error) { console.error("加载权限失败:", error); this.userPermissions = []; } finally { this.isLoading = false; } }, }, });
步骤2:预加载用户权限
在应用初始化或路由守卫中调用Action加载权限,确保组件渲染前权限已获取:
方式1:应用入口预加载(main.js)
import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' import { useAuthStore } from './stores/auth' const app = createApp(App) const pinia = createPinia() app.use(pinia) // 加载用户权限 const authStore = useAuthStore() authStore.fetchUserPerm() app.mount('#app')
方式2:路由守卫预加载(router/index.js)
import { useAuthStore } from '../stores/auth' router.beforeEach(async (to, from, next) => { const authStore = useAuthStore() // 若未加载过权限,先加载 if (authStore.userPermissions.length === 0 && !authStore.isLoading) { await authStore.fetchUserPerm() } next() })
步骤3:组件中实现同步权限校验
现在authStore.userPermissions是普通数组,无需处理Promise,直接实现同步校验逻辑:
方法版(适合动态传参的权限校验)
<script> // ... 其他导入 export default { setup() { const authStore = useAuthStore(); return { authStore }; }, methods:{ checkUserPermFn(value) { // 使用some方法判断是否存在匹配权限,性能比filter更优 return this.authStore.userPermissions.some(permission => permission.name.includes(value) ); }, }, }; </script> <!-- 模板中调用同步方法 --> <div v-if="checkUserPermFn('your-target-permission')"><p>Hi!</p></div>
计算属性版(适合固定权限的校验)
<script> // ... 其他导入 export default { setup() { const authStore = useAuthStore(); return { authStore }; }, computed:{ hasTargetPermission() { return this.authStore.userPermissions.some(permission => permission.name.includes('your-target-permission') ); }, }, }; </script> <!-- 模板中使用计算属性 --> <div v-if="hasTargetPermission"><p>Hi!</p></div>
可选:添加加载状态提示
<template> <div v-if="authStore.isLoading">加载中...</div> <div v-else> <div v-if="hasTargetPermission"><p>Hi!</p></div> </div> </template>
内容的提问来源于stack exchange,提问作者Sashawasright
相关产品推荐
相关产品推荐

