This article is the fifth part of the vUE series. The content of this article is not the same as before. It belongs to the actual combat. Those interested in previous articles in this series can click on the link below
- Vue Core – VDOM
- On Vue-Slot
- On Vue-Transition
- On Vue-Transition-Group
In the first two articles, we analyzed the design ideas of
The
is an abstract component and only applies to a single element. The
component transitions the list, and it renders a real element node. Both add transition effects to elements
Today I’m going to think about some of the things I’ve looked at before and combine them with a real business scenario.
1. Business background
My main job at the company is to support the shipping layer, which I wrote about a long time ago (in TypeScript + Big Projects in Action). In the normal development of some projects, the verification of various permissions is unavoidable.
At the service level, different people have different operations. For example, SRE has the permission to use SRE and can do a lot of things. Common RDS have their corresponding permissions, and most of the things they can do are just some basic operation and maintenance capabilities, and these are permissions under the services they are responsible for. And there are so many permissions checks, if you don’t do unified management, it’s going to be crazy.
Maybe this article should be called “How to Use Abstract Components to Manage Permission Operations in a unified way.” If you don’t want to see my whole business thought process, you can skip this chapter and go to the next one.
1. Routine
In this case, the first step is to ask the backend to throw the field related to the permission in the interface when obtaining the specific service information. Then the front end sets the permission value globally. The specific operations are as follows
vuex
interface State {
hasPermission: boolean
}
const state: State = {
hasPermission: false
}
const getters = {
hasPermisson: (state: State) = > state.hasPermisson
}
const mutations = {
SET_PERMISSON (state: State, hasPermisson: boolean) {
state.hasPermisson = hasPermisson
}
}
const actions = {
async srvInfo (context: { commit: Commit }, params: { appkey: string{})return request.get(`xxx/srv/${params.appkey}`)},/ * * * * * * * * * * * * *
async checkPermisson (context: { commit: Commit }, params? : { [key:string] :string{})return request.get('xxx/permission', { params: params })
}
}
export default {
state,
getters,
mutations,
actions
}
Copy the code
- Then proceed to the corresponding operation on the page
<template>
<div class="srv-page">
<el-button @click="handleCheck('type1')">Confirm permission 1</el-button>
<el-button @click="handleCheck('type2')">Confirm permission 2</el-button>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { Getter, Mutation, Action } from 'vuex-class'
@Component
export default class SrvPage extends Vue {
appkey: string = 'common-appkey'
@Getter('hasPermisson') hasPermisson: boolean
@Mutation('SET_PERMISSON') SET_PERMISSON: Function
@Action('srvInfo') srvInfo: Function
@Action('checkPermisson') checkPermisson: Function
getSrvInfo () {
this.srvInfo({ appkey: this.appkey }).then((res: Ajax.AjaxResponse) = > {
if (res.data.code === 0) {
this.SET_PERMISSON(true)}else {
this.SET_PERMISSON(false)
}
})
}
handleCheck (type: string) {
if (this.hasPermisson) {
this.checkPermisson({ type: type }).then((res: Ajax.AjaxResponse) = > {
if(res.data.code ! = =0) {
this.notify('xxx')}}}else {
this.notify('xxx') } } notify (name? : string) {this.$notify({
title: 'warning'.message: 'You do not have the operation rights, please contact the responsible person${name}Open permission '.type: 'warning'.duration: 5000}}})</script>
Copy the code
However, because the back-end interface for obtaining service information is connected to a number of three-party interfaces, the response speed of the interface is a little slow. As a result, some operations that do not need to wait for the specific service information will have a delay, and the user will see the default permission value.
2. Upgrade version
According to the above method of management, if there are fewer pages and fewer operations, it may be more applicable, which is also the practice at the beginning of the project, when the permissions on the page is still less, so I have not found any problems. However, with the increasing number of permissions related operations, it is found that the above approach is too small. In order to make myself better able to carry out the development and maintenance of the project in the future, I carried out another operation upgrade combined with the business.
If you have a lot of permissions on a lot of pages, can you take them out and make them into mixins? The answer is yes. And then I started to take those things and make mixins out of them
vuex
Existing part unchanged, add part of the operation
const state: State = {
isAppkeyFirstCheck: false
}
const getters = {
isAppkeyFirstCheck: (state: State) = > state.isAppkeyFirstCheck
}
const mutations = {
SET_APPKEY_FIRST_CHECK (state: State, firstCheck: boolean) {
state.isAppkeyFirstCheck = firstCheck
}
}
Copy the code
- Then, in
mixins/check-permission.ts
The logic is as follows: For the same service, we only do a common check and put the key parameters of the serviceappkey
use$route.query
Each change initializes the permissions, and the rest of the operation is very much the same as before
import { Vue, Component, Watch } from 'vue-property-decorator'
import { Action, Getter, Mutation } from 'vuex-class'
declare module 'vue/types/vue' {
interfaceVue { handleCheckPermission (params? : { appkey? :string, message? :string}) :Promise<any>}}@Component
export default class CheckPermission extends Vue {
@Getter('hasPermisson') hasPermisson: boolean
@Getter('isAppkeyFirstCheck') isAppkeyFirstCheck: boolean
@Mutation('SET_PERMISSON') SET_PERMISSON: Function
@Mutation('SET_APPKEY_FIRST_CHECK') SET_APPKEY_FIRST_CHECK: Function
@Action('checkPermisson') checkPermisson: Function
@Watch('$route.query.appkey')
onWatchAppkey (val: string) {
if (val) {
this.SET_APPKEY_FIRST_CHECK(true)
this.SET_PERMISSON(false) } } handleCheckPermission (params? : { appkey? :string, message? :string{})return new Promise((resolve: Function, reject: Function) = > {
if (!this.isAppkeyFirstCheck) {
if (!this.hasPermisson) {
this.notify('xxx')
}
resolve()
return
}
const appkey = params && params.appkey || this.$route.query.appkey
this.checkPermisson({ appkey: appkey }).then(res= > {
this.SET_APPKEY_FIRST_CHECK(false)
if (res.data.code === 0) {
this.SET_PERMISSON(true)
resolve(res)
} else {
this.SET_PERMISSON(false)
this.notify('xxx')
}
}).catch(error= >{ reject(error) }) }) } notify (name? :string) {
this.$notify({
title: 'warning'.message: 'You do not have the operation rights, please contact the responsible person${name}Open permission '.type: 'warning'.duration: 5000}}})Copy the code
- Finally we can use it in the page
<template>
<div class="srv-page">
<el-button @click="handleCheck('type1')">Operation 1</el-button>
<el-button @click="handleCheck('type2')">2 operation</el-button>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import CheckPermission from '@/mixins/check-permission'
@Component({
mixins: [ CheckPermission ]
}}
export default class SrvPage extends Vue {
handleCheck (type: string) {
this.handleCheckPermission().then(res= > {
console.log(type)
})
}
}
</script>
Copy the code
OK, at this point, it all looks good, and it does make it a lot easier to manage permissions
Second, TS actual combat
However, I find it troublesome to refer to mixins in many pages. Then I took it a step further and thought, is there a better way to manage? The answer, of course, is yes
After looking at vUE’s built-in component design idea, I thought, why don’t I take it and integrate it into my own business?
The keyword of this article is abstract component. My intention is not to render real nodes, but to use abstract component to encapsulate a layer, put all permissions in the component, and then execute the events of the child nodes after verification. However, since my actual business is developed in TS, vue does not seem to support writing abstract components in TS because it cannot set abstract properties for components. (I looked for a circle of information, really did not find how to support, if there are friends know, please inform me, thanks)
The scene was awkward at one point, so TO avoid embarrassment I had to do the next best thing and just render the real node, which is something like the
The idea is simple, and it’s divided into several steps
- in
render
Phase renders the node and binds the related events - right
children
The child node performs specific event processing - respectively
<permission>
和<permission-group>
component - Global Register component
1, the permission
First, implement the < Permission > component, which is primarily responsible for binding permission events to individual elements
<script lang="ts">
import { Vue, Component, Watch, Prop } from 'vue-property-decorator'
import { Action, Getter, Mutation } from 'vuex-class'
import { VNode } from 'vue'
@Component({
name: 'permission'
})
export default class Permission extends Vue {
@Prop({ default: 'span' }) tag: string
@Prop() appkey: string
@Prop() message: string
@Prop({ default: null }) param: { template_name: string, appkey? : string, env? : string } |null
@Getter('adminsName') adminsName: string
@Getter('hasPermisson') hasPermisson: boolean
@Getter('isAppkeyFirstCheck') isAppkeyFirstCheck: boolean
@Mutation('SET_PERMISSON') SET_PERMISSON: Function
@Mutation('SET_APPKEY_FIRST_CHECK') SET_APPKEY_FIRST_CHECK: Function
@Action('checkPermisson') checkPermisson: Function
@Action('isSlient') isSlient: Function
@Watch('$route.query.appkey')
onWatchAppkey (val: string) {
if (val) {
this.SET_APPKEY_FIRST_CHECK(true)
this.SET_PERMISSON(false)
}
}
render (h): VNode {
const tag = this.tag
const children: Array<VNode> = this.$slots.default
if (children.length > 1) {
console.warn(
'<permission> can only be used on a single element. Use ' +
'<permission-group> for lists.')}const rawChild: VNode = children[0]
this.handleOverride(rawChild)
return h(tag, null, [rawChild])
}
handleOverride (c: any) {
if(! (c.data && (c.data.on || c.data.nativeOn))) {return console.warn('there is no permission callback')}const method = c.data.on ? c.data.on.click : c.data.nativeOn.click
c.data.on && (c.data.on.click = this.handlePreCheck(method))
c.data.nativeOn && (c.data.nativeOn.click = this.handlePreCheck(method))
}
handlePreCheck (cb: Function) {
return () = > {
const {
appkey = this.$route.query.appkey,
message = ' '
} = this
this.handlePermissionCheck({ appkey, message }).then(() = > {
cb && cb()
})
}
}
handlePermissionCheck (params: { [key: string]: string }) {
return new Promise((resolve: Function, reject: Function) = > {
if (!this.isAppkeyFirstCheck) {
if (!this.hasPermisson) {
return this.$notify({
title: 'warning'.message: 'You do not have the service operation rights, please contact the service responsible person to open:The ${this.adminsName}`.type: 'warning'.duration: 5000})}if (this.param) {
return this.isSlient(this.param).then(res= > {
resolve(res)
})
}
resolve()
return
}
this.checkPermisson({ appkey: params.appkey || this.$route.query.appkey }).then(res= > {
this.SET_APPKEY_FIRST_CHECK(false)
if (res.data.code === 0) {
this.SET_PERMISSON(true)
if (this.param) {
return this.isSlient(this.param).then(slientRes= > {
resolve(slientRes)
})
}
resolve(res)
} else {
this.SET_PERMISSON(false)
this.$notify({
title: 'warning'.message: params.message || res.data.message,
type: 'warning'.duration: 5000
})
}
}).catch(error= > {
reject(error)
})
})
}
}
</script>
Copy the code
Then register globally
import Permission from 'components/permission.vue'
Vue.component('Permission', Permission)
Copy the code
If the
<template>
<div class="srv-page">
<permission>
<el-button @click.native="handleCheck('type1')">Permission Operations 1</el-button>
</permission>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class SrvPage extends Vue {
handleCheck (type: string) {
console.log(type)
}
}
</script>
Copy the code
2, the permission – group
In contrast to the
// The difference in the render part
render (h): VNode {
const tag = this.tag
const rawChildren: Array<VNode> = this.$slots.default || []
const children: Array<VNode> = []
for (let i = 0; i < rawChildren.length; i++) {
const c: VNode = rawChildren[i]
if (c.tag) {
children.push(c)
}
}
children.forEach(this.handleOverride)
return h(tag, null, children)
}
// The parameter part is different
const param = c.data.attrs ? c.data.attrs.param : null
Copy the code
Register globally
import PermissionGroup from 'components/permission-group.vue'
Vue.component('PermissionGroup', PermissionGroup)
Copy the code
Page using
<template>
<div class="srv-page">
<permission-group>
<el-button @click.native="handleCheck('type1')">Permission Operations 1</el-button>
<el-button @click.native="handleCheck('type2')">Permission Operation 2</el-button>
</permission-group>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class SrvPage extends Vue {
handleCheck (type: string) {
console.log(type)
}
}
</script>
Copy the code
So far, our permission interception component has been implemented. Although we originally wanted to use abstract components to accomplish this, but there is no way, vue using TS does not support abstract properties. However, after such processing, the management of the permission operation will become very easy, but also very easy to maintain.
Third, JS actual combat
We already know that Vue cannot write its own abstract components using TS, but JS can. For the JS implementation, the logic is basically the same, but the render phase is different, I will not list all the code. The same code is simply omitted
<script>
export default {
abstract: true
props: {
appkey: String.message: String.param: {
type: Object.default: () = > { return {} }
}
},
render (h) {
const children = this.$slots.default
if (children.length > 1) {
console.warn(
'<permission> can only be used on a single element. Use ' +
'<permission-group> for lists.')}const rawChild = children[0]
this.handleOverride(rawChild)
return rawChild
},
methods: {
handleOverride (c) {
// ...
},
handlePreCheck (cb) {
// ...
},
handlePermissionCheck (param) {
// ...}}}</script>
Copy the code
The same goes for
conclusion
So far, the abstract components that belong to our own business have been implemented. In the actual business, there are many businesses worth thinking about, to explore better ways to achieve, such as we can pull out a shake or throttling component, which is also very common in the business.
A little chicken soup at the end of the article:
- Basically, about 80% of our technology growth is driven by the business that we’re in charge of. How much that drives you really depends on how much you think about the business
- Don’t lament how repetitive and boring your own projects are. No matter where you go, it’s boring to look at the business when you don’t own equity
- Try to make yourself the owner, and then plug in, and you can see a lot of things
- The only way to grow is your own way, by taking the time to study something and then integrate it with your business; Or learn through business
- Will learn to practice in the business, you can remember more firmly, this should be the right way to grow
- Once you’ve mastered that, maybe it’s time to learn the soft skills of how to write a document or a powerpoint presentation
And finally, a bunch of my own
Front end communication group: 731175396, welcome to join hi