自定义指令
# 自定义指令
# 介绍
在Vue的模板语法中我们学习过各种各样的指令:v-show、v-for、v-model等等,除了使用这些指令之外,Vue 也允许我们来自定义自己的指令。
- 注意:在Vue中,代码的复用和抽象主要还是通过组件;
- 通常在某些情况下,你需要对DOM元素进行底层操作,这个时候就会用到自定义指令;
自定义指令分为两种 :
- 自定义局部指令:组件中通过 directives 选项,只能在当前组件中使用;
- 自定义全局指令:app的 directive 方法,可以在任意组件中被使用;
比如我们来做一个非常简单的案例:当某个元素挂载完成后可以自定获取焦点
- 实现方式一:如果我们使用默认的实现方式;
- 实现方式二:自定义一个 v-focus 的局部指令;
- 实现方式三:自定义一个 v-focus 的全局指令;
# 默认的实现方式
<template>
<div>
<input type="text" ref="input">
</div>
</template>
<script>
import { ref, onMounted } from "vue";
export default {
setup() {
const input = ref(null);
onMounted(() => {
input.value.focus();
})
return {
input
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 局部自定义指令
- 在组件 options 选项中使用 directives 即可
- 它是一个对象,在对象中编写我们自定义指令的名称(注意:这里不需要加
v-) - 自定义指令有一个生命周期,是在组件挂载后调用的 mounted,我们可以在其中完成操作
<template>
<div>
<input type="text" v-focus>
</div>
</template>
<script>
export default {
// 局部指令
directives: {
focus: {
mounted(el, bindings, vnode, preVnode) {
console.log("focus mounted");
el.focus();
}
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 全局自定义指令 🔥
上面的局部自定义指令修改:
main.js
import { createApp } from 'vue'
const app = createApp(App)
app.directive("focus", {
mounted(el, bindings, vnode, preVnode) {
console.log("focus mounted");
el.focus();
}
})
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
然后直接使用即可!!!
# 指令的生命周期、参数、修饰符 🔥
一个指令定义的对象,Vue提供了如下的几个钩子函数:
- created:在绑定元素的 attribute 或事件监听器被应用之前调用;
- beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用;
- mounted:在绑定元素的父组件被挂载后调用;
- beforeUpdate:在更新包含组件的 VNode 之前调用;
- updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用;
- beforeUnmount:在卸载绑定元素的父组件之前调用;
- unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次;
在我们的生命周期中,我们可以通过 bindings 获取到对应的参数(value)和修饰符对象(modifiers)
<template>
<div>
<button v-if="counter < 2" v-why.aaaa.bbbb="'coderwhy'" @click="increment">当前计数: {{counter}}</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
// 局部指令
directives: {
why: {
// preVnode 在 updated 节点会有值
created(el, bindings, vnode, preVnode) {
console.log("why created", el, bindings, vnode, preVnode);
console.log(bindings.value);// 字符串
console.log(bindings.modifiers);// 是个对象
},
beforeMount() {
console.log("why beforeMount");
},
mounted() {
console.log("why mounted");
},
beforeUpdate() {
console.log("why beforeUpdate");
},
updated() {
console.log("why updated");
},
beforeUnmount() {
console.log("why beforeUnmount");
},
unmounted() {
console.log("why unmounted");
}
}
},
setup() {
const counter = ref(0);
const increment = () => counter.value++;
return {
counter,
increment
}
}
}
</script>
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
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
# 时间戳格式化指令 🔥🔥🔥
开发要求:
- 在开发中,大多数情况下从服务器获取到的都是时间戳;
- 我们需要将时间戳转换成具体格式化的时间来展示;
- 在Vue2中我们可以通过过滤器来完成;
- 在Vue3中我们可以通过 计算属性(computed) 或者 自定义一个方法(methods) 来完成;
- 其实我们还可以通过一个自定义的指令来完成;
实现一个可以自动对时间格式化的指令v-format-time
/src/directives/index.js
import registerFormatTime from './format-time';
export default function registerDirectives(app) {
registerFormatTime(app);
}
1
2
3
4
5
2
3
4
5
/src/directives/format-time.js
import dayjs from 'dayjs'
export default function (app) {
app.directive('format-time', {
created(el, bindings) {
bindings.formatString = 'YYYY-MM-DD HH:mm:ss'
if (bindings.value) {
bindings.formatString = bindings.value
}
},
// 都放入 mounted 也一样!
mounted(el, bindings) {
const textContent = el.textContent
let timestamp = parseInt(textContent)
if (textContent.length === 10) {
timestamp = timestamp * 1000
}
el.textContent = dayjs(timestamp).format(bindings.formatString)
},
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/src/main.js
import registerDirectives from './directives'
registerDirectives(app)
1
2
2
编辑 (opens new window)
上次更新: 2022/03/24, 16:45:21