首页前端开发VUE面试官问我按钮级别权限怎么控制,我说v-if,面试官说再见(面试权限表)

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

时间2023-03-24 17:28:37发布访客分类VUE浏览422
导读:最近的面试中有一个面试官问我按钮级别的权限怎么控制,我说直接v-if啊,他说不够好,我说我们项目中按钮级别的权限控制情况不多,所以v-if就够了,他说不够通用,最后他对我的评价是做过很多东西,但是都不够深入,好吧,那今天我们就来深入深入。因...

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

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

获取权限码

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

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);

        }

    }

}
    )

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

函数方式

使用示例如下:

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

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组件,使用示例如下:

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组件的实现。

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包裹的内容,否则就啥也不渲染。

指令方式

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

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

实现如下:

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钩子:

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钩子里通过这个方法将用户权限数据和按钮的更新方法关联起来,这样当用户权限数据改变了,可以自动触发按钮的重新渲染:

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方法的重新运行。

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

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

vue.js渲染

若转载请注明出处: 面试官问我按钮级别权限怎么控制,我说v-if,面试官说再见(面试权限表)
本文地址: https://pptw.com/jishu/214.html
开发者必备的 7 款效率提升工具! 字节前端二面react面试题(边面边更)_2023-03-13(字节前端社招二面)

游客 回复需填写必要信息