自定义指令

750 词

自定义指令#

内置指令:

  • v-html
  • v-model
  • v-for

自定义指令:

  • v-focus
  • v-loading
  • v-lazy

每个指令都有各自独立的功能
自定义指令:自己定义的指令,可以封装一些 dom 操作,扩展额外功能

实例:元素获得焦点#

需求:当页面加载时,让元素获得焦点(autofocus在 safari 浏览器有兼容性问题)
操作 dom:dom 元素.focus()方式比较麻烦

1
2
3
mounted(){
this.$refs.inp.focus()
}

注册自定义指令语法#

  1. 全局注册(main.js 中注册)
1
2
3
4
5
6
7
Vue.directive('指令名', {
// inserted会在指令所在的元素被插入到页面中时触发
inserted(el) {
// el就是指令所绑定的元素,可以对el标签扩展额外功能
el.focus();
},
});
  1. 局部注册(组件内注册)
1
2
3
4
5
6
7
8
directives:{
指令名:{
inserted(el){
// 可以对el标签扩展额外功能
el.focus()
}
}
}
  1. 使用
1
<input v-指令名 type="text" />

指令的值#

需求:实现一个 color 指令,传入不同的颜色,给标签设置文字颜色

  • 语法:在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值
1
<div v-color="color">content</div>
  • 通过binding.value可以拿到指令值,指令值修改会触发 update 函数
1
2
3
4
5
6
7
8
9
10
directives:{
color:{
inserted(el,binding){
el.style.color=binding.value
},
update(el,binding){
el.style.color=binding.value
}
}
}

实例:v-loading 指令封装#

场景:实际开发过程中,发送请求需要时间,在请求数据未回来时,页面会处于空白状态,用户体验不好
需求:封装一个 v-loading 指令,实现加载中的效果
分析:

  1. 本质loading效果就是一个蒙层,盖在了盒子上
  2. 数据请求中,开启loading状态,添加蒙层
  3. 数据请求完毕,关闭loading状态,移除蒙层

实现:

  1. 准备一个loading类,通过伪元素定位,设置宽高,实现蒙层
  2. 开启关闭loading状态,本质只需要添加移除类即可
  3. 结合自定义指令的语法进行封装复用

完整代码:

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<template>
<div class="box" v-loading="isLoading">
<ul>
<li v-for="item in list" :key="item.id" class="news">
<div class="left">
<div class="title">{{ item.title }}</div>
<div class="info">
<span>{{ item.source }}</span>
<span>{{ item.time }}</span>
</div>
</div>

<div class="right">
<img :src="item.img" alt="" />
</div>
</li>
</ul>
</div>
</template>

<script>
// 安装axios => yarn add axios
import axios from 'axios';

// 接口地址:http://hmajax.itheima.net/api/news
// 请求方式:get
export default {
data() {
return {
list: [],
isLoading: true,
};
},
async created() {
// 1. 发送请求获取数据
const res = await axios.get('http://hmajax.itheima.net/api/news');

setTimeout(() => {
// 2. 更新到 list 中,用于页面渲染 v-for
this.list = res.data.data;
this.isLoading = false;
}, 2000);
},
directives: {
loading: {
inserted(el, binding) {
binding.value ? el.classList.add('loading') : el.classList.remove('loading');
},
update(el, binding) {
binding.value ? el.classList.add('loading') : el.classList.remove('loading');
},
},
},
};
</script>

<style>
.loading::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff url(./assets/loading.gif) no-repeat center;
}
.box {
width: 800px;
min-height: 500px;
border: 3px solid orange;
border-radius: 5px;
position: relative;
}
.news {
display: flex;
height: 120px;
width: 600px;
margin: 0 auto;
padding: 20px 0;
cursor: pointer;
}
.news .left {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-right: 10px;
}
.news .left .title {
font-size: 20px;
}
.news .left .info {
color: #999999;
}
.news .left .info span {
margin-right: 20px;
}
.news .right {
width: 160px;
height: 120px;
}
.news .right img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>