One command to release 1000 lines of Shishan code

Big factory technology advanced front-end Node advanced

Click above for Programmer Growth Guide and follow the official account

Reply 1, join the advanced Node communication group

Preface

Hello everyone, I am Koala. In the development of our business projects, we will encounter the need for button-level permission control

This article is more about sharing and experience, because the business requirements this time are not general permission instructions.

For example, in our internal business, we obtain permission information by by page~

Business scenario recurrence

Dear friends, let’s take a look at the directory structure of Shishan Code that I have encountered so far.

Directory structure before optimization

  • views

    • A.vue

    • B.vue

    • c.vue

    • D.vue

  • mixins

    • btnCodeMap.js

    • btnMixins.js

Case introduction

Let’s take the A page new button permission as an example to introduce the functions of these files.

  • A.vue page file

html
Copy code
<el-button v-if="!hideBtnAdd">Add</el-button>
  • btnCodeMap.js button permission configuration file

js
Copy code
// A page button permissions,
export const btnCodeMapA = [{
  title: 'Add new to main table',
  resourceCode: 'add-A',
  hideBack: (self) => {
    self.hideBtnAdd = true
  }
}, {
  title: 'Main Table Edit',
  resourceCode: 'edit-A',
  hideBack: (self) => {
    self.hideBtnEdit = true
  }
}]
// If you add a new page later, you will continue to configure it here...
  • btnMixin.js button permission request file

js
Copy code
import {btnCodeMapA} from './btnCodeMap.js'
export const btnMixin = {
    data(){
        return {
            hideBtnAdd:false // Each time a permission is added, a new variable must be added...
        }
    },
    created(){
        // Get backend permissions omitted...
        const permissionList = await getPermissionList()
        // Get the configuration permission mapping of page A here, which is btnCodeMapA
        const btnCodeMapA = this.getMap()
        // Then traverse various loops to get the list without permission hideList, and execute the hideBack callback
        const hideList = btnCodeMapA.forEach(()=>permissionList.forEach()...)
        hideList.forEach((item)=>item.hideBack(this))
    }
}

In other words, changing the permissions of a button requires changing three files, and the naming needs to correspond one to one.

Yanzu, you must be almost unable to withstand it after seeing this, right Who can withstand this… Even if you want me to close it with one hand

I looked at it yesterday

btnCodeMap.js has been piled up like this…

196f15f22cf47c3948dfe2cb0cf9822d.jpegbtnMixin.js 已经这样了

dd1ef8aae38bcce84c448fb4543249d1.jpeg

image.png

In a few months, it will probably be like this…

6482a697b5ab81c0a8749fd81622d952.jpeg

image.png

The most distressing thing I heard from my colleagues was My roller is tired

Command implementation

In fact, what the author thought at first was relatively simple. A simple permission command can solve the problem.

But because our permission data is requested based on the page, it will still be slightly different.

Ideal use

js
Copy code
<span v-permission="audit">Audit</span>

Initial version

  • permission.js

js
Copy code
const permissionList = getPerimissionList() // Get backend permission data
export const permission = {
  inserted(el, binding, vnode, oldVnode) {
      // At this time, a data asynchronous problem was discovered, and the data has not been returned yet.
  }
}

Practical issues

Let’s share the author’s experience with three difficulties encountered in actual development.

1. Data asynchronous problem

2. Solve the problem of Duplicate requests for multiple instructions

3. Solve the problem of removing multiple same code nodes in a single column of the table

Only part of the core code is shown below, you can directly view the full version

Data asynchronous problem

In fact, the author has thought of several solutions to this problem.

1. Use route guard to intercept the request, and then next jump

This method will cause the page to be stuck with a white screen in the scenario where the request times out (although the chance is unlikely…)

2. Directly request inside the instruction inserted hook

js
Copy code
export const permission = {
  inserted(el, binding, vnode, oldVnode) {
      await getPerimissionList()
  }
}

In the end, I chose Option 2. Although it sacrificed some versatility, I personally think it is more in line with the principle of High cohesion

Repeated request issue for multiple instructions

Imagine if there are multiple instructions in a page

Then the inserted hook will be entered multiple times

In other words, getPerimissionList will be called multiple times

js
Copy code
<span v-permission="audit">Audit</span>
Cancel audit
Confirm

At this point we need to maintain a permissionObj to store page permissions

js
Copy code
const permissionObj = Object.create(null)
export const permission = {
    inserted(el, binding, vnode, oldVnode) {
        const res = await getPerimissionList()
        const { viewId } = router.history.current.meta //Our internal business data, each route is unique
        permissionObj[viewId] = res
     }
    
}

Seeing this, don’t you guys still understand that this has nothing to do with resolving duplicate requests?

Indeed, I didn’t think of a good solution at first, but since permissionObj can store data, can’t it also store Promise?

js
Copy code
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 //Our internal business data, each route is unique
        const {value} = binding
        //Indicates that the current page is requesting data
        if(permissionObj[viewId] instanceof Promise) return
        
        // The current page already has permission data, operate directly
        if(permissionObj[viewId]){
          handleElPermission(permissionObj[viewId],el)
          return
        }
        
        const p = getPerimissionList()
        permissionObj[viewId] = p //Copy Promise to permissionObj[viewId] to indicate requesting
        
        p.then(res=>{
            permissionObj[viewId] = res
            handleElPermission(el,res[value])
        }).catch(e=>{
            permissionObj[viewId] = null
        })
        
     }
    
}

The problem of removing multiple nodes with the same code

This scenario exists in tables. For example, in the operation column of the table, there is an Edit button in each row.

Then there are 10 edit buttons for 10 rows of data

In other words, when the permission interface comes back, we need to operate these 10 buttons

js
Copy code
<el-button v-permission="edit">Edit</el-button>

A difficult problem we encountered at this time is how to store so many dom nodes.

Yanzu, can you stop and think about any good plans?

The author later solved this problem using the Map data structure

js
Copy code
const codeElementMap = new Map()
export const permission = {
    inserted(el, binding, vnode, oldVnode) {
        const {value:code} = binding
        // Create code el mapping to store 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
        })
     }
    
}

Complete code

js
Copy code

import router from '../router/'
import { getBtnList } from '@/api/user' // Business interface

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 // No code, no processing

    const { viewId } = router.history.current.meta

    if (permissionObj[viewId] instanceof Promise) return //The current page is requesting permission data to prevent repeated requests

    // Permission data already exists on the current page. Operate directly.
    if (permissionObj[viewId]) {
      const elPermission = permissionObj[viewId][code]
      handleElPermission(el, elPermission)
      return
    }

    // Create code el mapping
    if (codeElementMap.has(code)) return codeElementMap.get(code).push(el)
    else codeElementMap.set(code, [el])

    //Set the permissions of each button on the current page
    const request = getCurrentPermission()
    permissionObj[viewId] = request

    request.then((res) => {
      //Set the permission corresponding to viewId {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
    })
  },
  //When unbinding, release the memory corresponding to code
  unbind(el, binding, vnode) {
    const code = binding.value
    codeElementMap.delete(code)
  }
}

Write at the end

It is inevitable to encounter hill code in business projects, but there must be someone who carries forward

Luckily we caught it in time and avoided it

But dear fellows, please don’t try it easily~

Because if you improve it, you won’t get results, if you improve it, it will be your big pot…

Limited personal ability

If there are any mistakes, please correct me If it helps, I suggest you be careful with three thumbs in a row

Author: front-end scalpel
Link: https://juejin.cn/post/7274072378606223415
Source: Rare Earth Nuggets

Conclusion

Node community


I have formed a Node.js community with a very good atmosphere. There are many Node.js friends in it. If you are interested in learning Node.js (you can also have plans in the future), we can do Node.js-related work together. Exchange, learn and build together. Add Koala friends below and reply “Node”.

7ab766aedcb2b475a7c3ead5536acee7.png

"Share, Like, Watch" to support

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Vue entry skill tree Home page Overview 39301 people are learning the system