Table of contents
First briefly introduce the custom command
Built-in directives and custom directives
Compared with v-if, the advantages are as follows
checkPermission check permission
Supplement other auxiliary methods
First briefly introduce the custom command
Before introducing the Permission command, let's briefly understand what a custom command is.
Vue's custom directives Vue.directive
are created through methods.
-
bind
: Called when the directive is bound to the element for the first time. A one-time initial setup can be performed here. -
inserted
: Called when the bound element is inserted into the parent element. Note that the parent element may not yet exist, so DOM manipulation cannot be performed. -
update
: Called when the component where the bound element is located is updated, but may occur before its child components are updated. You can compare the values before and after the update and perform corresponding operations. -
componentUpdated
: Called after the component where the bound element is located and its subcomponents are all updated. Actions can be performed, such as updating the DOM. -
unbind
: Called when the directive is unbound from the element. Cleanup operations can be performed.
In addition to the above hook functions, other properties and methods can also be defined in the instruction object:
-
name
: The name of the directive, used to bind the directive in the template. -
value
: The binding value of the directive, which can be an expression or a variable. -
arg
: The parameter of the command, used to pass additional information. -
modifiers
: Modifier object, which can be used to pass additional tag information. -
el
: The element to which the directive is bound. -
vm
: The Vue instance to which the directive belongs. -
expression
: The expression of the instruction. -
context
: The Vue instance of the context where the directive is located.
Here is an example of a simple custom directive:
Vue.directive('my-directive', {
bind(el, binding, vnode) {
// 初始化设置
},
inserted(el, binding, vnode) {
// 元素插入父元素时调用
},
update(el, binding, vnode) {
// 组件更新时调用
},
componentUpdated(el, binding, vnode) {
// 组件及子组件更新后调用
},
unbind(el, binding, vnode) {
// 解绑时调用
}
});
Use custom directives in templates:
<div v-my-directive="value"></div>
Built-in directives and custom directives
The commonly used built-in instructions are
v-if
,v-show
,v-bind
,v-for
etc., which have been used by everyone, so I won’t go into details here.
ask a question first
Q: When is the Vue custom instruction executed?
A: It is parsed and executed at runtime
What exactly happened between the built-in and the custom directive?
It may be because I didn't have a deep understanding before, and I always thought that both built-in instructions and custom instructions were resolved at compile time.
From the figure below, it can be seen 编译时
that the built-in instruction Render
has been executed at that time, but 运行时
the parsing of the custom instruction starts after that.
Just give an example
Frequently encountered interview questions, "What's the difference between v-if and v-show", the difference is not a simple one. The former is to directly delete the DOM, and the latter is to change the display attribute.
v-if
VNode
It has been judged in the template compilation stage before generation , and v-show
it is also parsed at compile time, but v-if
the rendering elements are determined at compile time, and v-show
displayed and hidden according to conditions at runtime.
15.1.1 中 v-if 指令的原理概述
The following is an excerpt of part of the code in "Introduction to Vue.js in simple terms"
v-if
<!-- 模板 -->
<li v-if="has">if</li>
<li v-else>else</li>
// 编译后
(has)
? _c('li',[_v("if")])
: _c('li',[_v("else")])
When has is true, the first li element will be created VNode
.
-
_c
Refers to createElement, which is used to create component nodes. -
_v
Is used to create text VNode
v-show
Let's take a look again v-show
, it is obviously an instruction, which render
is the same as when we write instructions in a function, but in this way, v-if must be a syntactic sugar, because it is not a real instruction.
That is to say, the timing of custom instructions cannot be parsed before the built-in instructions (of course, if you directly modify the compile time or other tricky operations through vue-template-compiler, just take it for granted).
Interested friends can go to vue-template-explorer.netlify.app/ to try
After we really understand the execution timing of the built-in instructions, we will write a custom instruction next.
Write a Permission command
Common permission granularity
Before implementing the Permission command, we need to understand the common granularity of permissions. Below are some common scenarios.
The common schemes are
-
Encapsulate the button into a component, and then pass parameters through props to control whether it is displayed or operable
-
Encapsulate a component, pass the button through the slot form, and then pass the value control through the prop
-
Write a custom instruction to control whether the element is displayed by value
Compared with v-if, the advantages are as follows
-
The permission control function can be easily extended.
-
Support multiple permission codes, support asynchronous access to permissions, etc.
-
The semantic effect is more obvious.
How to achieve
Write a simple display and hide control first, which can be controlled by responsive data.
// 全局自定义指令
Vue.directive("permission", {
// 在元素被插入到 DOM 中时触发
inserted(el, binding) {
// 如果绑定值为 false,则从父节点中移除元素
if (!binding.value) {
el.parentNode.removeChild(el); // 移除元素
}
},
// 在元素更新时触发
update(el, binding) {
// 如果绑定值为 true
if (binding.value) {
// 如果元素没有父节点(即之前被移除了)
if (!el.parentNode) {
// 将元素插入到原来的位置
el.__v_originalParent.insertBefore(el, el.__v_anchor || null);
}
} else {
// 如果元素有父节点
if (el.parentNode) {
// 创建一个注释节点作为替换元素的占位符
el.__v_anchor = document.createComment("");
el.__v_originalParent = el.parentNode;
// 用注释节点替换原来的元素
el.parentNode.replaceChild(el.__v_anchor, el);
}
}
},
});
In inserted
the hook, when the element is inserted into the DOM, according to the state of the binding value, if yes false
, remove the element from the parent node to achieve the hidden effect.
In update
the hook, when the element is updated, according to the state of the binding value, if it is true
and the element was removed before (no parent node), then insert the element to the original position to achieve the display effect. If the binding value is false
and the element currently has a parent node, create a comment node as a placeholder for the replacement element, and replace the original element with the comment node to achieve the hidden effect.
How to use
The way to use is very simple, just pass in the required value in the command.
<template>
<div id="app">
isVisible: {
{ isVisible }}
<button v-permission="isVisible">自定义指令</button>
<button @click="isVisible = !isVisible">Toggle</button>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
isVisible: true,
};
},
};
</script>
Effect
It can be seen that the effect is basically the v-if
same as that of . Of course, this is a runtime operation, and there are still differences in principle.
But if it's just a simple variable, ours v-permission
doesn't make much sense. It can really be controlled directly v-if
, because we need asynchronous requests and unified management. This command will be strengthened next.
Strengthen the function
There are more than 4 buttons on the table page in the common middle and background, as shown in the figure below, including adding, deleting, editing, and querying. From the business, and out of the business.
Generally, it is necessary to perform permission control under a certain module, function, or certain route with add
| delete
| update
| query
, or some other operations.
So our button permission should be 页面::新增
, and then we need a checkPermission
method to provide support.
Assume that all button-level permissions can be obtained in a certain API, and the format is consistent with the format agreed by the server-side classmates.
{
"orders": [
"add",
"update",
"delete",
"query",
"detail",
"enable",
"disable"
]
}
In the above JSON, orders is used as the order page, and the corresponding Actions that can be operated are placed in the array. If it does not exist, it is considered that there is no such permission.
Consumer
The input from the consumer orders::update
means that it needs to obtain订单的编辑权限
<button v-permission="'orders::update'">Update</button>
instruction
In the instruction area, we need to change the hook of the instruction (the example here is only adjusted in inserted) to asynchronous waiting. When we first enter the inserted
hook, we need to display
set it to none
, or bind
do this in the hook, otherwise there will be a flash effect.
checkPermission
Then call the incoming value here binding.value
, that is orders::update
, wait for the return value here checkPermission
, and then determine whether this element needs to be deleted.
Vue.directive("permission", {
async inserted(el, binding) {
// 默认先改为 none,等数据回来之后再进行操作,你通过class来控制
el.style.display = "none";
const hasPermission = await checkPermission(binding.value);
el.style.display = "";
if (!hasPermission) {
el.parentNode?.removeChild(el); // 移除元素
}
},
...
});
checkPermission check permission
Receive orders::update
, by splitPermissionString
dividing into orders
and update
.
Then send the request, but if there are multiple calls to a page at the same time v-permission
, there may be a problem of repeated requests.
So use controller.hasRequested
and controller.task
to control the repeatability of the request. This is necessary, and there is currently JavaScript
no similar (lock mechanism) found in and Java
in wait/notify
. So it's just a lock mechanism that imitates Java
const controller = {
// 是否发过请求
hasRequested: false,
// 权限集合
permissionList: [],
// 真正的请求任务
task: null,
};
const checkPermission = async (value = null) => {
// 截取对应的模块和操作
const [module = null, operate = null] = splitPermissionString(value) ?? [];
// 判断模块和操作是否存在
if (!module || !operate) return false;
// 判断是否发送过请求
if (!controller.hasRequested) {
controller.hasRequested = true;
controller.task = getPermissionsApi();
}
// 获取权限数据,进行赋值
controller.permissionList = await controller.task ?? [];
// 判断是否有权限
return controller.permissionList[module]?.includes(operate) ?? false;
};
look at the effect
It can be seen that it is hidden by default before sending the request, and then waits for the interface data to come back, re-determines which ones do not have permission, and then controls the display or deletes the element node.
Supplement other auxiliary methods
// 模拟请求
const getPermissionsApi = async () =>
fetch(`/api.json?t=${new Date().getTime()}`).then((res) => res.json());
// 分割字符串
const splitPermissionString = (str) => {
try {
if (typeof str === "string" && str.includes("::")) {
const [firstPart, secondPart] = str.split("::");
return [firstPart.trim(), secondPart.trim()];
} else {
throw new Error("Invalid permission string or delimiter not found");
}
} catch (error) {
console.error(error.message);
return [];
}
};
share
Finally, I would like to recommend a platform for learning the front end, and you can watch all the videos after opening the black gold card! It only costs 39 yuan.
There is contact information below