公众号搬运公众号搬运一个指令,释放 1000 行屎山代码
小u一个指令,释放 1000 行屎山代码
前言
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心~ 在我们业务项目开发中,会遇到按钮级权限控制
的需求
本文更多的是分享和经历,因为这次的业务需求,并不是通用的权限指令
比如我们内部业务中是按页面
来获取权限信息~
业务场景复现
彦祖们, 下面就来看下笔者目前遇到的屎山代码
的目录结构
优化前目录结构
views
- A.vue
- B.vue
- C.vue
- D.vue
- …
mixins
- btnCodeMap.js
- btnMixins.js
案例介绍
下面 我们以 A页面新增按钮
权限为例,来介绍一下这些文件的作用
1 2 3
| html 复制代码 <el-button v-if="!hideBtnAdd">新增</el-button>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| js 复制代码 // A 页面 button 权限 , export const btnCodeMapA = [{ title: '主表新增', resourceCode: 'add-A', hideBack: (self) => { self.hideBtnAdd = true } }, { title: '主表编辑', resourceCode: 'edit-A', hideBack: (self) => { self.hideBtnEdit = true } }] // 之后新增页面 就要在这里继续配置...
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| js 复制代码 import {btnCodeMapA} from './btnCodeMap.js' export const btnMixin = { data(){ return { hideBtnAdd:false // 每增加一个权限都要新增一个变量... } }, created(){ // 获取后端权限 省略... const permissionList = await getPermissionList() // 此处获取 A 页面的配置权限映射, 也就是 btnCodeMapA const btnCodeMapA = this.getMap() // 然后各种循环遍历 拿到无权限的列表 hideList, 并执行 hideBack 回调 const hideList = btnCodeMapA.forEach(()=>permissionList.forEach()...) hideList.forEach((item)=>item.hideBack(this)) } }
|
也就是说一个按钮权限的变动,需要改动三处文件,并且命名需要一一对应
彦祖们 想必你们看到这里已经快顶不住了吧😭 这谁顶得住啊…要我也是一手关闭
昨天看了下
btnCodeMap.js
已经堆成这样了…

btnMixin.js
已经这样了
image.png
再过几个月 估计就是这样了…
image.png
听到同事最痛心的一句话就是 我的滚轮都滚累了
指令实现
其实笔者一开始想的还是比较简单的,一个简单的权限指令就能解决了
但是因为我们的权限数据是根据页面请求的,所以还是会稍有不同。
理想的使用方式
1 2 3
| js 复制代码 <span v-permission="audit">审核</span>
|
最初版本
1 2 3 4 5 6 7 8
| js 复制代码 const permissionList = getPerimissionList() // 获取后端权限数据 export const permission = { inserted(el, binding, vnode, oldVnode) { // 此时发现了数据异步问题,数据还没返回 } }
|
实际问题
下面就实际开发遇到的三个难点 分享一下笔者的经历
1.数据异步问题
2.解决多个指令的 重复请求
问题
3.解决表格单列多个 相同 code
节点移除问题
下面只展示了部分核心代码,彦祖们可以直接看完整版本
数据异步问题
其实这个问题 笔者想了好几种方案
1.使用路由守卫来拦截请求,之后 再 next
跳转
这种方式 在请求超时的场景下,会导致页面白屏卡段(虽然几率不大…)
2.直接在指令 inserted
钩子内部请求
1 2 3 4 5 6 7
| js 复制代码 export const permission = { inserted(el, binding, vnode, oldVnode) { await getPerimissionList() } }
|
最终还是选了 方案2
虽然牺牲了部分通用性, 但是个人认为更符合 高内聚
的原则
多个指令的重复请求问题
想象一下,如果一个页面中存在多个指令
那么就会多次 进入 inserted
钩子
也就是说会调用多次 getPerimissionList
1 2 3 4 5
| js 复制代码 <span v-permission="audit">审核</span> <span v-permission="unAudit">取消审核</span> <span v-permission="confirm">确认</span>
|
此时 我们就要维护一个 permissionObj
用来存储页面权限
1 2 3 4 5 6 7 8 9 10 11
| js 复制代码 const permissionObj = Object.create(null) export const permission = { inserted(el, binding, vnode, oldVnode) { const res = await getPerimissionList() const { viewId } = router.history.current.meta //我们内部的业务数据,每个路由唯一 permissionObj[viewId] = res } }
|
看到这里 彦祖们是不是还不是很明白 这和解决重复请求有半毛钱关系?
的确一开始 笔者也没想到好的解决方案,但是 permissionObj
既然能存储数据,那不也就能存储 Promise
吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| js 复制代码 const permissionObj = Object.create(null) const handleElPermission = (el, permission) => { if (permission === false) el.remove() } export const permission = { inserted(el, binding, vnode, oldVnode) { const { viewId } = router.history.current.meta //我们内部的业务数据,每个路由唯一 const {value} = binding // 说明当前页面正在请求数据 if(permissionObj[viewId] instanceof Promise) return // 当前页面已经有权限数据 直接操作 if(permissionObj[viewId]){ handleElPermission(permissionObj[viewId],el) return } const p = getPerimissionList() permissionObj[viewId] = p // 把 Promise 复制给permissionObj[viewId] 表示正在请求 p.then(res=>{ permissionObj[viewId] = res handleElPermission(el,res[value]) }).catch(e=>{ permissionObj[viewId] = null }) } }
|
多个相同 code 节点移除问题
这种场景存在表格中,比如表格的操作列中 每一行存在一个 编辑
按钮
那么 10 行数据就有 10 个编辑按钮
也就是说 等权限接口回来,我们需要操作这 10 个按钮
1 2 3
| js 复制代码 <el-button v-permission="edit">编辑</el-button>
|
此时我们遇到的一个难题就是, 用什么方式来存储这么多的 dom 节点
彦祖们 可以停下来 想一下有什么好的方案?
笔者后来用 Map
数据结构 解决了这个问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| js 复制代码 const codeElementMap = new Map() export const permission = { inserted(el, binding, vnode, oldVnode) { const {value:code} = binding // 建立 code el 映射,用来存储 el if (codeElementMap.has(code)) return codeElementMap.get(code).push(el) else codeElementMap.set(code, [el]) p.then(res=>{ permissionObj[viewId] = res const els = codeElementMap.get(code) els.forEach(el=>{ handleElPermission(el,res[code]) }) }).catch(e=>{ permissionObj[viewId] = null }) } }
|
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| js 复制代码
import router from '../router/' import { getBtnList } from '@/api/user' // 业务接口
const permissionObj = Object.create(null) const codeElementMap = new Map()
const getCurrentPermission = () => { const { viewId } = router.history.current.meta return new Promise(resolve => { getBtnList(viewId).then(resolve) }) }
const handleElPermission = (el, permission) => { if (permission === false) el.remove() }
export const permission = { inserted(el, binding, vnode, oldVnode) { const { value: code } = binding
if (!code) return // 无 code 不处理
const { viewId } = router.history.current.meta
if (permissionObj[viewId] instanceof Promise) return // 当前页面正在请求权限数据,防止重复请求
// 当前页面已经存在权限数据 直接操作 if (permissionObj[viewId]) { const elPermission = permissionObj[viewId][code] handleElPermission(el, elPermission) return }
// 建立 code el 映射 if (codeElementMap.has(code)) return codeElementMap.get(code).push(el) else codeElementMap.set(code, [el])
// 设置当前页面各按钮权限 const request = getCurrentPermission() permissionObj[viewId] = request
request.then((res) => { // 设置 viewId 对应的 permission {viewId:{code:true|false}} const viewPermission = (res.data ?? []).reduce((prev, { permission, resourceCode }) => { prev[resourceCode] = permission return prev }, {})
permissionObj[viewId] = viewPermission
const elPermission = viewPermission[code] const elements = codeElementMap.get(code) elements.forEach(el => { handleElPermission(el, elPermission) }) }).catch(() => { permissionObj[viewId] = null }) }, // 解绑时候, 释放 code 对应的内存 unbind(el, binding, vnode) { const code = binding.value codeElementMap.delete(code) } }
|
写在最后
业务项目中 遇到 屎山代码
是难以避免的,不过总得有人负重前行
幸运的是 我们及时发现并避免了它
但是各位彦祖们 请不要轻易尝试~
因为 改好了没业绩, 改坏了就是你的大锅…
个人能力有限
如有不对,欢迎指正🌟 如有帮助,建议小心心大拇指三连🌟