# 自定义指令
参数:
{string} name
{Function | Object} [definition]
生命周期钩子 可以在第二个参数中使用
created() {}
等一系列的生命周期钩子。const myDirective = { // 在绑定元素的 attribute 前 // 或事件监听器应用前调用 created(el, binding, vnode, prevVnode) { // 下面会介绍各个参数的细节 }, // 在元素被插入到 DOM 前调用 beforeMount(el, binding, vnode, prevVnode) {}, // 在绑定元素的父组件 // 及他自己的所有子节点都挂载完成后调用 mounted(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件更新前调用 beforeUpdate(el, binding, vnode, prevVnode) {}, // 在绑定元素的父组件 // 及他自己的所有子节点都更新后调用 updated(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件卸载前调用 beforeUnmount(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件卸载后调用 unmounted(el, binding, vnode, prevVnode) {} }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
binding 中的参数:
- instance:使用指令的组件实例。
- value:传递给指令的值。例如,在 v-my-directive="1 + 1" 中,该值为 2。
- oldValue:先前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否有更改都可用。
- arg:传递给指令的参数(如果有的话)。例如在 v-my-directive:foo 中,arg 为 "foo"。
- modifiers:包含修饰符(如果有的话) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}。
- dir:一个对象,在注册指令时作为参数传递。
<div id="dynamic-arguments-example" class="demo">
<p>Scroll down the page</p>
<p v-pin="200">Stick me 200px from the top of the page</p>
</div>
1
2
3
4
2
3
4
// 全局指令
const app = Vue.createApp({})
app.directive('pin', {
mounted(el, binding) {
el.style.position = 'fixed'
// binding.value 是我们传递给指令的值——在这里是 200
el.style.top = binding.value + 'px'
}
})
app.mount('#dynamic-arguments-example')
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
// 组件内指令
<script setup>
// 在模板中启用 v-focus
const vFocus = {
mounted: (el) => el.focus()
}
</script>
<template>
<input v-focus />
</template>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 实践 permission指令
实现一个按钮级别的颗粒权限控制指令。
// directive.js
const permissions = ["base:soft:add", "base:soft:cut"]
app.directive('permission', (el, binding) => {
const value = binding.value
const isBind=Reflect.has(permissions, "*:*:*")||Reflect.has(permissions, value)
if (!isBind) {
el.parentNode && el.parentNode.removeChild(el)
}
})
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
// mian.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 引入全局指令
import { directive } from './directive/directive'
// 全局注册
// 注意:要放在 const app = createApp(App) 之后
directive(app)
app.mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 实践 v-loading
# vue2
import Vue from "vue";
import loading from "@/components/control/v-loading/loading.vue";
const LoadingComponents = Vue.extend(loading);
const insertDom = (parent, el) => {
parent.appendChild(el.mask);
};
const toggleLoading = (el, binding) => {
if (binding.value) {
Vue.nextTick(() => {
// el.instance.visible = true;
insertDom(el, el);
});
} else {
// el.instance.visible = false;
el.removeChild(el.instance.$el);
}
};
export default {
bind(el, binding, vnode) {
const mask = new LoadingComponents({
el: document.createElement("div"),
data() {},
});
el.style.position = "relative";
el.instance = mask;
el.mask = mask.$el;
el.mask.style.position = "absolute";
el.mask.style.background = binding.arg || "#F8F8F8CC";
el.mask.style.top = 0;
el.mask.style.left = 0;
binding.value && toggleLoading(el, binding);
},
update(el, binding) {
if (binding.oldValue !== binding.value) {
toggleLoading(el, binding);
}
},
unbind(el, binding) {
el.instance && el.instance.$destroy();
},
};
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
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
<template>
<div class="loadingContent">
<div class="loading">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</template>
<script>
export default {
components: {},
data() {
return {};
},
computed: {},
watch: {},
methods: {},
created() {},
mounted() {},
};
</script>
<style lang="css" scoped>
.loadingContent {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.loading {
position: relative;
width: 100px;
height: 15px;
}
.loading span {
position: absolute;
display: inline-block;
width: 15px;
height: 100%;
margin-right: 5px;
background: #3485f8;
-webkit-animation: load 1.04s ease infinite;
}
.loading span:last-child {
margin-right: 0px;
}
@-webkit-keyframes load {
0% {
opacity: 1;
-webkit-transform: scale(1.2);
}
100% {
opacity: 0.2;
-webkit-transform: scale(0.2);
}
}
.loading span:nth-child(1) {
-webkit-animation-delay: 0.13s;
}
.loading span:nth-child(2) {
left: 20px;
-webkit-animation-delay: 0.26s;
}
.loading span:nth-child(3) {
left: 40px;
-webkit-animation-delay: 0.39s;
}
.loading span:nth-child(4) {
left: 60px;
-webkit-animation-delay: 0.52s;
}
.loading span:nth-child(5) {
left: 80px;
-webkit-animation-delay: 0.65s;
}
</style>
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
70
71
72
73
74
75
76
77
78
79
80
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
70
71
72
73
74
75
76
77
78
79
80
import vLoadingDirective from "@/components/control/v-loading/index";
Vue.directive("loading", vLoadingDirective);
1
2
2
# vue3
import { Spin } from 'ant-design-vue'
import { createVNode, render } from 'vue'
const appendEl = (el) => {
el.appendChild(el.instance)
}
const removeEl = (el) => {
el.removeChild(el.instance)
}
const loadingDirective = {
name: 'v-loading',
mounted(el, binding) {
const div = document.createElement('div')
div.setAttribute(
'class',
'absolute top-0 left-0 w-full h-full flex-row justify-center items-center'
)
render(createVNode(Spin), div)
el.instance = div
el.style.position = 'relative'
if (binding.value) {
appendEl(el)
}
},
updated(el, binding) {
if (binding.value !== binding.oldValue) {
binding.value ? appendEl(el) : removeEl(el)
}
}
}
export default loadingDirective
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
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