最近面试,面试官问我按钮级权限怎么控制,我直接说v-if,他说不够好,我说我们项目中按钮级的权限控件不多,所以v-if就够了,他说不够一般,最后他对我的评价是我有做了很多事情,但是还不够深入,好吧,那今天我们再深入一点。因为自己没有相关实践,所以还是从16.2kstar的后台管理系统项目vuevbenadmin来看下具体如何操作吧。获取权限码做权限控制需要一个代码,不管是权限码还是角色码。一般后端会一次性返回,然后全局存储。登录成功后获取并保存vuevbenadmin。到全球商店:import{defineStore}from'pinia';exportconstusePermissionStore=defineStore({state:()=>({//权限代码列表permCodeList:[],}),getters:{//获取getPermCodeList(){returnthis.permCodeList;},},actions:{//storesetPermCodeList(codeList){this.permCodeList=codeList;},//请求权限代码asyncchangePermissionCode(){constcodeList=awaitgetPermCode();this.setPermCodeList(codeList);}}})接下来,它提供了三个按钮级的权限控制方法,我们一一来看。使用函数方法的例子如下:has[20000,2000010]代码可见本质上是通过v-if,而是通过统一的权限判断方法hasPermission:导出函数usePermission(){functionhasPermission(value,def=true){//默认情况下考虑权限if(!value){returndef;}constallCodeList=permissionStore.getPermCodeList;如果(!isArray(value)){返回allCodeList.includes(value);}//intersection是lodash提供的一种方法,用于返回存在于所有给定数组中的元素数组return(intersection(value,allCodeList)).length>0;返回真;}}很简单,从globalstore中获取获取当前用户的权限码列表,然后判断是否有当前按钮需要的权限码。如果有多个权限码,只要满足其中一个,除了function方法外,还可以使用component方法,也可以使用component方法,Vuevbenadmin提供了一个Authority组件,使用方法示例如下:使用Authority包裹需要权限控制的按钮。按钮需要的权限码是通过value属性传入的。接下来我们看一下Authority组件的实现。同样使用hasPermission方法,如果当前用户拥有按钮所需要的权限码,则Authority包的内容将原样渲染,否则不渲染任何内容.最后一个命令模式是命令模式。使用示例如下:visiblewithcode['1000']permission实现如下:import{usePermission}from'/@/hooks/web/usePermission';functionisAuth(el,binding){const{hasPermission}=usePermission();constvalue=binding.value;如果(!值)返回;if(!hasPermission(value)){el.parentNode?.removeChild(el);}}constmounted=(el,binding)=>{isAuth(el,binding);};constauthDirective={//绑定元素的父组件//及其所有子节点挂载后,调用mounted,};//注册全局指令导出函数setupPermissionDirective(app){app.directive('auth',authDirective);}只定义了一个mountedhook,即绑定元素挂载后调用,仍然使用hasPermission方法判断当前用户是否拥有命令插入的按钮所需要的权限码。如果不存在,则直接移除绑定的元素。很明显,Vuevbenadmin的实现存在两个问题。一是按钮的权限不能动态改变,二是动态改变当前用户的权限不会生效。解决第一个问题很简单,因为上面的逻辑只是删除??元素,没有添加回来的逻辑,所以添加一个更新的钩子:app.directive("auth",{mounted:(el,binding)=>{constvalue=binding.valueif(!value)returnif(!hasPermission(value)){//挂载时没有删除元素的权限removeEl(el)}},updated(el,binding){//的按钮权限码没有变化,不处理if(binding.value===binding.oldValue)return//判断用户权限状态是否和上次一样,不需要处理binding.oldValue)letnewHasPermission=hasPermission(binding.value)if(oldHasPermission===newHasPermission)return//如果有权限,则将元素添加回去if(newHasPermission){addEl(el)}else{//如果有是没有权限,删除元素removeEl(el)}},})consthasPermission=(value)=>{return[1,2,3].includes(value)}constremoveEl=(el)=>{//在绑定元素级元素上存储父节点el._parentNode=el.parentNode//在绑定元素上存储注释节点el._placeholdrNode=document.createComment("auth")//使用注释节点代替el.parentNode?.replaceChild(el._placeholderNode,el)}constaddEl=(el)=>{//自己替换占位符注释nodeel._parentNode?.replaceChild(el,el._placeholderNode)}主要是保存父节点,不然想加回来的时候获取不到原来的父节点,删除的时候自己创建一个注释节点取一个座位,这样下次你想回去的时候,你可以知道你在哪里第二个问题的原因是修改了用户权限数据,但是不会触发按钮的重新渲染,所以我们需要想办法让它触发。这可以使用watchEffect方法来完成,我们可以在更新的钩子中使用它。权限数据关联按钮的更新方法,这样当用户权限数据改变时,可以自动触发按钮的重新渲染:import{createApp,reactive,watchEffect}from"vue"constcodeList=reactive([1,2,3])consthasPermission=(value)=>{returncodeList.includes(value)}app.directive("auth",{updated(el,binding){letupdate=()=>{让valueNotChange=binding.value===binding.oldValue让oldHasPermission=hasPermission(binding.oldValue)让newHasPermission=hasPermission(binding.value)让permissionNotChange=oldHasPermission===newHasPermission如果(valueNotChange&&permissionNotChange)返回El如果(newHasPermission(el)重新添加{}(el)}};if(el._watchEffect){update()}else{el._watchEffectfect=watchEffect(()=>{update()})}},})将updatedhook中的更新逻辑提取到一个update方法中,然后在watchEffect中执行第一次update,这样用户权限的响应数据就是它可以与更新方法相关联。后续用户权限数据变化,可以自动触发更新方法的重新运行。嗯,深入学习之后,好像还是挺简单的。不确定这些是不是面试官想要的,或者还有其他更高级更优雅的实现。有知道的可以指点一下,不胜感激。