面试官问我按钮级别权限怎么控制,我说v-if,面试官说再见

文章通过分析Vue项目Vuevbenadmin中的实现,详细介绍了三种按钮级别的权限控制方式:函数方式、组件方式和指令方式。每种方式都基于全局存储的权限码列表进行判断,确保用户具有相应权限才能显示按钮。此外,文章还指出了现有实现的两个问题,并提出了改进方案,包括处理权限动态变化的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近的面试中有一个面试官问我按钮级别的权限怎么控制,我说直接v-if啊,他说不够好,我说我们项目中按钮级别的权限控制情况不多,所以v-if就够了,他说不够通用,最后他对我的评价是做过很多东西,但是都不够深入,好吧,那今天我们就来深入深入。

因为我自己没有相关实践,所以接下来就从这个有16.2k星星的后台管理系统项目Vue vben admin中看看它是如何做的。

获取权限码

要做权限控制,肯定需要一个code,无论是权限码还是角色码都可以,一般后端会一次性返回,然后全局存储起来就可以了,Vue vben admin是在登录成功以后获取并保存到全局的store中:

 

js

复制代码

import { defineStore } from 'pinia'; export const usePermissionStore = defineStore({ state: () => ({ // 权限代码列表 permCodeList: [], }), getters: { // 获取 getPermCodeList(){ return this.permCodeList; }, }, actions: { // 存储 setPermCodeList(codeList) { this.permCodeList = codeList; }, // 请求权限码 async changePermissionCode() { const codeList = await getPermCode(); this.setPermCodeList(codeList); } } })

接下来它提供了三种按钮级别的权限控制方式,一一来看。

函数方式

使用示例如下:

 

html

复制代码

<template> <a-button v-if="hasPermission(['20000', '2000010'])" color="error" class="mx-4"> 拥有[20000,2000010]code可见 </a-button> </template> <script lang="ts"> import { usePermission } from '/@/hooks/web/usePermission'; export default defineComponent({ setup() { const { hasPermission } = usePermission(); return { hasPermission }; }, }); </script>

本质上就是通过v-if,只不过是通过一个统一的权限判断方法hasPermission

 

js

复制代码

export function usePermission() { function hasPermission(value, def = true) { // 默认视为有权限 if (!value) { return def; } const allCodeList = permissionStore.getPermCodeList; if (!isArray(value)) { return allCodeList.includes(value); } // intersection是lodash提供的一个方法,用于返回一个所有给定数组都存在的元素组成的数组 return (intersection(value, allCodeList)).length > 0; return true; } }

很简单,从全局store中获取当前用户的权限码列表,然后判断其中是否存在当前按钮需要的权限码,如果有多个权限码,只要满足其中一个就可以。

组件方式

除了通过函数方式使用,也可以使用组件方式,Vue vben admin提供了一个Authority组件,使用示例如下:

 

html

复制代码

<template> <div> <Authority :value="RoleEnum.ADMIN"> <a-button type="primary" block> 只有admin角色可见 </a-button> </Authority> </div> </template> <script> import { Authority } from '/@/components/Authority'; import { defineComponent } from 'vue'; export default defineComponent({ components: { Authority }, }); </script>

使用Authority包裹需要权限控制的按钮即可,该按钮需要的权限码通过value属性传入,接下来看看Authority组件的实现。

 

js

复制代码

<script lang="ts"> import { defineComponent } from 'vue'; import { usePermission } from '/@/hooks/web/usePermission'; import { getSlot } from '/@/utils/helper/tsxHelper'; export default defineComponent({ name: 'Authority', props: { value: { type: [Number, Array, String], default: '', }, }, setup(props, { slots }) { const { hasPermission } = usePermission(); function renderAuth() { const { value } = props; if (!value) { return getSlot(slots); } return hasPermission(value) ? getSlot(slots) : null; } return () => { return renderAuth(); }; }, }); </script>

同样还是使用hasPermission方法,如果当前用户存在按钮需要的权限码时就原封不动渲染Authority包裹的内容,否则就啥也不渲染。

指令方式

最后一种就是指令方式,使用示例如下:

 

js

复制代码

<a-button v-auth="'1000'" type="primary" class="mx-4"> 拥有code ['1000']权限可见 </a-button>

实现如下:

 

js

复制代码

import { usePermission } from '/@/hooks/web/usePermission'; function isAuth(el, binding) { const { hasPermission } = usePermission(); const value = binding.value; if (!value) return; if (!hasPermission(value)) { el.parentNode?.removeChild(el); } } const mounted = (el, binding) => { isAuth(el, binding); }; const authDirective = { // 在绑定元素的父组件 // 及他自己的所有子节点都挂载完成后调用 mounted, }; // 注册全局指令 export function setupPermissionDirective(app) { app.directive('auth', authDirective); }

只定义了一个mounted钩子,也就是在绑定元素挂载后调用,依旧是使用hasPermission方法,判断当前用户是否存在通过指令插入的按钮需要的权限码,如果不存在,直接移除绑定的元素。

很明显,Vue vben admin的实现有两个问题,一是不能动态更改按钮的权限,二是动态更改当前用户的权限也不会生效。

解决第一个问题很简单,因为上述只有删除元素的逻辑,没有加回来的逻辑,那么增加一个updated钩子:

 

js

复制代码

app.directive("auth", { mounted: (el, binding) => { const value = binding.value if (!value) return if (!hasPermission(value)) { // 挂载的时候没有权限把元素删除 removeEl(el) } }, updated(el, binding) { // 按钮权限码没有变化,不做处理 if (binding.value === binding.oldValue) return // 判断用户本次和上次权限状态是否一样,一样也不用做处理 let oldHasPermission = hasPermission(binding.oldValue) let newHasPermission = hasPermission(binding.value) if (oldHasPermission === newHasPermission) return // 如果变成有权限,那么把元素添加回来 if (newHasPermission) { addEl(el) } else { // 如果变成没有权限,则把元素删除 removeEl(el) } }, }) const hasPermission = (value) => { return [1, 2, 3].includes(value) } const removeEl = (el) => { // 在绑定元素上存储父级元素 el._parentNode = el.parentNode // 在绑定元素上存储一个注释节点 el._placeholderNode = document.createComment("auth") // 使用注释节点来占位 el.parentNode?.replaceChild(el._placeholderNode, el) } const addEl = (el) => { // 替换掉给自己占位的注释节点 el._parentNode?.replaceChild(el, el._placeholderNode) }

主要就是要把父节点保存起来,不然想再添加回去的时候获取不到原来的父节点,另外删除的时候创建一个注释节点给自己占位,这样下次想要回去能知道自己原来在哪。

第二个问题的原因是修改了用户权限数据,但是不会触发按钮的重新渲染,那么我们就需要想办法能让它触发,这个可以使用watchEffect方法,我们可以在updated钩子里通过这个方法将用户权限数据和按钮的更新方法关联起来,这样当用户权限数据改变了,可以自动触发按钮的重新渲染:

 

js

复制代码

import { createApp, reactive, watchEffect } from "vue" const codeList = reactive([1, 2, 3]) const hasPermission = (value) => { return codeList.includes(value) } app.directive("auth", { updated(el, binding) { let update = () => { let valueNotChange = binding.value === binding.oldValue let oldHasPermission = hasPermission(binding.oldValue) let newHasPermission = hasPermission(binding.value) let permissionNotChange = oldHasPermission === newHasPermission if (valueNotChange && permissionNotChange) return if (newHasPermission) { addEl(el) } else { removeEl(el) } }; if (el._watchEffect) { update() } else { el._watchEffect = watchEffect(() => { update() }) } }, })

updated钩子里更新的逻辑提取成一个update方法,然后第一次更新在watchEffect中执行,这样用户权限的响应式数据就可以和update方法关联起来,后续用户权限数据改变了,可以自动触发update方法的重新运行。

好了,深入完了,看着似乎也挺简单的,我不确定这些是不是面试官想要的,或者还有其他更高级更优雅的实现呢,知道的朋友能否指点一二,在下感激不尽。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值