vue利用自界说指令实现页面按钮权限控制

打印 上一主题 下一主题

主题 919|帖子 919|积分 2761

vue自界说指令先容

Vue 除了内置的一系列指令 (好比 v-model 或 v-show) 之外,Vue 还允许你注册自界说的指令
一个自界说指令由一个包含类似组件生命周期钩子的对象来界说。钩子函数会吸收到指令所绑定元素作为其参数。下面是一个自界说指令的例子,当一个 input 元素被 Vue 插入到 DOM 中后,它会被自动聚焦:
  1. <script setup>
  2. // 在模板中启用 v-focus
  3. const vFocus = {
  4.   mounted: (el) => el.focus()
  5. }
  6. </script>
  7. <template>
  8.   <input v-focus />
  9. </template>
复制代码
在 <script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自界说指令。在上面的例子中,vFocus 即可以在模板中以 v-focus 的情势利用。
在没有利用 <script setup> 的情况下,自界说指令必要通过 directives 选项注册:
  1. export default {
  2.   setup() {
  3.     /*...*/
  4.   },
  5.   directives: {
  6.     // 在模板中启用 v-focus
  7.     focus: {
  8.       /* ... */
  9.     }
  10.   }
  11. }
复制代码
将一个自界说指令全局注册到应用层级也是一种常见的做法:
  1. const app = createApp({})
  2. // 使 v-focus 在所有组件中都可用
  3. app.directive('focus', {
  4.   /* ... */
  5. })
复制代码
指令钩子

一个指令的界说对象可以提供几种钩子函数 (都是可选的):
  1. const myDirective = {
  2.   // 在绑定元素的 attribute 前
  3.   // 或事件监听器应用前调用
  4.   created(el, binding, vnode) {
  5.     // 下面会介绍各个参数的细节
  6.   },
  7.   // 在元素被插入到 DOM 前调用
  8.   beforeMount(el, binding, vnode) {},
  9.   // 在绑定元素的父组件
  10.   // 及他自己的所有子节点都挂载完成后调用
  11.   mounted(el, binding, vnode) {},
  12.   // 绑定元素的父组件更新前调用
  13.   beforeUpdate(el, binding, vnode, prevVnode) {},
  14.   // 在绑定元素的父组件
  15.   // 及他自己的所有子节点都更新后调用
  16.   updated(el, binding, vnode, prevVnode) {},
  17.   // 绑定元素的父组件卸载前调用
  18.   beforeUnmount(el, binding, vnode) {},
  19.   // 绑定元素的父组件卸载后调用
  20.   unmounted(el, binding, vnode) {}
  21. }
复制代码
  只有当所需功能只能通过直接的 DOM 操纵来实现时,才应该利用自界说指令。其他情况下应该尽大概地利用 v-bind 如许的内置指令来声明式地利用模板,如许更高效,也对服务端渲染更友好。
  钩子参数

指令的钩子会通报以下几种参数:


  • el:指令绑定到的元素。这可以用于直接操纵 DOM。
  • binding:一个对象,包含以下属性。

    • value:通报给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。
    • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
    • arg:通报给指令的参数 (假如有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。
    • modifiers:一个包含修饰符的对象 (假如有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
    • instance:利用该指令的组件实例。
    • dir:指令的界说对象。

  • vnode:代表绑定元素的底层 VNode。
  • prevVnode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
举例来说,像下面如许利用指令:
  1. <div v-example:foo.bar="baz">
复制代码
binding 参数会是一个如许的对象:
  1. {
  2.   arg: 'foo',
  3.   modifiers: { bar: true },
  4.   value: /* `baz` 的值 */,
  5.   oldValue: /* 上一次更新时 `baz` 的值 */
  6. }
复制代码
和内置指令类似,自界说指令的参数也可以是动态的。举例来说:
  1. <div v-example:[arg]="value"></div>
复制代码
  除了 el 外,其他参数都是只读的,不要更改它们。若你必要在差别的钩子间共享信息,保举通过元素的 dataset attribute 实现。
  简化情势

对于自界说指令来说,一个很常见的情况是仅仅必要在 mounted 和 updated 上实现相同的行为,除此之外并不必要其他钩子。这种情况下我们可以直接用一个函数来界说指令,如下所示:
  1. <div v-color="color"></div>
复制代码
  1. app.directive('color', (el, binding) => {
  2.   // 这会在 `mounted` 和 `updated` 时都调用
  3.   el.style.color = binding.value
  4. })
复制代码
对象字面量

假如你的指令必要多个值,你可以向它通报一个 JavaScript 对象字面量。别忘了,指令也可以吸收任何合法的 JavaScript 表达式。
  1. <div v-demo="{ color: 'white', text: 'hello!' }"></div>
复制代码
  1. app.directive('demo', (el, binding) => {
  2.   console.log(binding.value.color) // => "white"
  3.   console.log(binding.value.text) // => "hello!"
  4. })
复制代码
在组件上利用

当在组件上利用自界说指令时,它会始终应用于组件的根节点,和透传 attributes 类似。
  1. <MyComponent v-demo="test" />
复制代码
  1. <!-- MyComponent 的模板 -->
  2. <div> <!-- v-demo 指令会被应用在此处 -->
  3.   <span>My component content</span>
  4. </div>
复制代码
  必要注意的是组件大概含有多个根节点。当应用到一个多根组件时,指令将会被忽略且抛出一个告诫。和 attribute 差别,指令不能通过 v-bind=“$attrs” 来通报给一个差别的元素。
  利用自界说指令实现页面按钮权限控制

实现思绪:


  • 用户登录成功后, 在 router.beforeEach() 发起请求获取用户所拥有的权限,并将权限存储到 store 中
  • 初始化 hasBtnPermi 对象, 并导入到入口文件 mian.js
  • 在钩子函数 componentUpdated 判定是否有该按钮权限, 没有就用 removeChild() 方法移除按钮元素
在路由守卫获取按钮权限
  1. /**
  2. * permission.js
  3. */
  4. import router from './router';
  5. import store from './store';
  6. const whiteList = ['/login', '/404'];
  7. import { getSessStore } from '@/util/store';
  8. router.beforeEach((to, from, next) => {
  9.   let token = getSessStore('token');
  10.   if (token) {
  11.     /* has token*/
  12.     if (to.path === '/login') {
  13.       next({ path: '/' });
  14.     } else {
  15.       store.dispatch('GenerateRoutes').then(() => { // 获取按钮权限
  16.               next({ ...to, replace: true }); // hack方法 确保addRoutes已完成
  17.       })
  18.     }
  19.   } else {
  20.     if (whiteList.indexOf(to.path) !== -1) {
  21.           next()
  22.         } else {
  23.           next('/login');
  24.     }
  25.   }
  26. })
复制代码
配置store
  1. /**
  2. * store/index.js
  3. */
  4. import Vue from 'vue'
  5. import Vuex from 'vuex'
  6. import user from './modules/user'
  7. import common from './modules/common'
  8. import tags from './modules/tags'
  9. import leave from './modules/leave'
  10. import checkin from './modules/checkin'
  11. import getters from './getters'
  12. Vue.use(Vuex)
  13. const store = new Vuex.Store({
  14.   modules: {
  15.     user,
  16.     common,
  17.     tags,
  18.     leave,
  19.     checkin
  20.   },
  21.   getters,
  22. })
  23. export default store
复制代码
配置getters.js
  1. /**
  2. * store/getters.js
  3. */
  4. const getters = {
  5.     buttons: state => state.leave.buttons,
  6. }
  7. export default getters
复制代码
leave.js
  1. /**
  2. * store/modules/leave.js
  3. */
  4. import { getMenuByLogin } from '@/api/menu';
  5. import Vue from "vue";
  6. const checkIn = {
  7.     state: {
  8.         buttons: [],
  9.     },
  10.     mutations: {
  11.         SET_BUTTONS: (state, buttons) => {
  12.             state.buttons = buttons
  13.         },
  14.     },
  15.     actions: {
  16.         // 生成路由
  17.         GenerateRoutes({ commit }, type) {
  18.             return new Promise((resolve) => {
  19.                 // 向后端请求路由数据
  20.                 getMenuByLogin(type).then(({ data }) => {
  21.                     let btns = [];
  22.                     data.data.forEach((v) => {
  23.                         if (v.code) {
  24.                             btns.push(v.code);
  25.                         }
  26.                     })
  27.                     console.log('按钮--------', btns);
  28.                     commit('SET_BUTTONS', btns);
  29.                     resolve(btns);
  30.                 })
  31.                 // })
  32.             })
  33.         }
  34.     }
  35. }
复制代码
  store 文件结构如下图所示
  

配置自界说指令
  1. /**
  2. * btnPermi.js
  3. */
  4. import store from "@/store";
  5. const hasBtnPermi = {
  6.   // 钩子函数
  7.   bind(el, binding, vnode, oldVnode) {
  8.     // 指令绑定到元素上时调用
  9.   },
  10.   inserted(el, binding, vnode, oldVnode) {
  11.     // 被绑定元素插入父节点时调用
  12.   },
  13.   update(el, binding, vnode, oldVnode) {
  14.     // 被绑定于元素所在的模板更新时调用
  15.   },
  16.   componentUpdated(el, binding, vnode, oldVnode) {
  17.     // 被绑定元素所在模板完成一次更新周期时调用
  18.     const { value } = binding; // 获取页面上自定义指令v-hasBtnPermi传入的值, 如: btn_one
  19.     const permissions = store.getters.buttons; // 从vuex获取所有按钮权限数组
  20.     if (permissions.length > 0) {
  21.       const hasPermissions = permissions.includes(value); // 从权限列表里查找是否有该按钮权限
  22.           
  23.       if (!hasPermissions) { // 如果没有权限, 移除当前的按钮元素
  24.         el.parentNode && el.parentNode.removeChild(el);
  25.       }
  26.     }
  27.   },
  28.   unbind(el, binding, vnode, oldVnode) {
  29.     // 指令与元素解绑时调用
  30.   }
  31. };
  32. const install = function(Vue) {
  33.     Vue.directive('hasBtnPermi', hasBtnPermi)
  34. }
  35. if (window.Vue) {
  36.   window['hasBtnPermi'] = hasBtnPermi
  37.   Vue.use(install); // eslint-disable-line
  38. }
  39. export default install
复制代码
  btnPermi.js 文件需引入入口文件 main.js 内里
  1. /**
  2. * main.js
  3. */
  4. import "./permission"; // 权限
  5. import btnPermi from "./btnPermi.js";
  6. Vue.use(btnPermi );
复制代码
页面上利用
  1. <!-- btn_one 为按钮唯一字段 -->
  2. <el-button v-hasBtnPermi="'btn_one'"  size="small" @click="departure">按钮</el-button>
复制代码
总结

用户登录成功后, 在 router.beforeEach() 发起请求获取用户所拥有的权限,并将权限存储到 store 中, 初始化 hasBtnPermi 对象, 并导入到入口文件 mian.js , 在 componentUpdated 钩子函数内获取页面上自界说指令v-hasBtnPermi传入的值, 去权限数组里查找是否有改按钮权限,假如没有就利用 DOM 原生方法移 removeChild() 除移除当前的按钮元素

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王國慶

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表