# vue 的指令与过滤器

# 指令的概念

指令 (Directives) 是 vue 为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构

vue 中的指令按照不同的用途可以分为如下 6 大类 :

  1. 内容渲染指令
  2. 属性绑定指令
  3. 事件绑定指令
  4. 双向绑定指令
  5. 条件渲染指令
  6. 列表渲染指令

# 指令

# 内容渲染指令

内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有三个

  • v-text
  • {{}}
  • v-html

# v-text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div id="app">
<p v-text="username"></p>
<p v-text="gender">性别</p>
</div>

<script src="./lib/vue-2.6.12.js"></script>

<script>
const vm = new Vue({
el: '#app',
data: {
username: "zhangsan",
gender: "male"
}
});

</script>
</body>

** 注意 : **v-text 会覆盖标签内原有的内容

# {{}} 语法

vue 提供的 {{}} 语法,专门用来解决 v-text 会覆盖默认文本内容的问题。这种 {{}} 语法的专业名称是插值表达式 (Mustache)

1
2
<p>姓名 : {{username}}</p>
<p>性别 : {{gender}}</p>

** 注意 : ** 在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容

# v-html

v-text 指令和插值表达式只能渲染纯文本内容。如果要把包含 HTML 标签的字符串渲染为页面的 HTML 元素,则需要用到 v-html 这个指令

1
<p v-html="info"></p>

# 属性绑定指令

如果需要为元素的属性动态绑定属性值,则需要用到 v-bind 属性绑定指令.

1
2
<input type="text" :placeholder="tips">
<img :src="photo" alt="vue">

简写是英文的 :

# 使用 JavaScript 表达式

在 vue 提供的模板渲染语法中,除了支持绑定简单的数据值外,还支持 JavaScript 表达式的运算

1
2
3
4
5
6
7
{{number + 1}}

{{ok ? 'YES' : 'NO'}}

{{message.split('').reverse.join('')}}

<div v-bind:id="'list-' + id"></div>

# 事件绑定指令

vue 提供了 v-on 事件绑定指令,用来为 DOM 元素绑定事件监听

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
<body>
<div id="app">
<p>count的值是 : {{count}}</p>
<button v-on:click="add(4)">+1</button>
<button @click="sub">-1</button>
</div>

<script src="./lib/vue-2.6.12.js"></script>

<script>
const vm = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add(n=1) {
this.count+=n
},

sub() {
this.count--
}
}
})
</script>
</body>

在绑定事件处理函数的时候,可以使用 () 传递参数

v-on 可以简写为 @

# $event

vue 提供了内置变量,名字叫做 $event , 它就是原生 DOM 的事件对象 e, 如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event

1
2
3
4
5
6
<button v-on:click="add(4, $event)">+1</button>

add(n=1, e) {
this.count+=n
e.target.style.backgroundColor='red'
}

# 事件修饰符

在事件处理函数中调用 event.preventDefault () 或 event.stopPropagation () 是非常常见的需求。因此 vue 提供了事件修饰符的概念,来辅助程序员更方便的对事件的触发进行控制

事件修饰符说明
.prevent阻止默认行为
.stop阻止事件冒泡
.capture以捕获模式触发当前的事件处理函数
.once绑定的事件只触发一次
.self只有在 event.target 是当前元素自身时触发事件处理函数

# 按键修饰符

在监听键盘事件时,经常需要判断详细的按键,此时,可以为键盘相关的事件添加按键修饰符

1
2
3
4
<!-- 只有在key是enter时调用vm.submit()-->
<input @keyup.enter="submit">
<!-- 只有在key是esc时调用vm.clearinput()-->
<input @keyup.esc="clearInput">

# 双向绑定指令

vue 提供了 v-model 双向数据绑定指令,可以在不操作 DOM 的前提下,快速获取表单数据

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
<body>
<div id="app">
<form>
<input type="text" name="username" v-model="username">
<select name="province" v-model="city">
<option value="">请选择城市</option>
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">广州</option>
<option value="4">深圳</option>
</select>
</form>
</div>

<script src="lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
username: '张三',
city: ''
}
})
</script>
</body>

** 注意 : ** 这里的 v-model 记住是双向绑定,无论是 data 数据源的变化还是界面值的变化,都是双向同步的,而之前学的 v-bind 只是将 data 数据源同步到界面上,而界面上的值变化不会引起 data 数据源中的值产生变化

常用标签

  • input 标签
  • textarea
  • select

可以认为,有 value 属性的标签都可以用 v-model

# v-model 指令的修饰符

为了方便对用户输入的内容进行处理,vue 为 v-model 指令提供了 3 个修饰符

修饰符作用示例
.number自动将用户的输入值转为数值类型
.trim自动过滤用户输入的首尾空白字符
.lazy在 “change” 时而非 “input” 时更新 (失去焦点才更新数据)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<div id="app">
<input type="text" name="number1" v-model.number="n1">
<input type="text" name="number2" v-model.number="n2">
<input type="text" name="lazy" v-model.lazy="name">
<textarea name="area" cols="30" rows="10" v-model.trim="msg"></textarea>
<p>{{n1 + n2}}</p>
<p>{{msg}}</p>
</div>

<script src="lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
n1: null,
n2: null,
msg: null,
name: null
}
})
</script>
</body>

# 条件渲染指令

  1. v-show 的原理是:动态为元素添加或移除 display: none 样式,来显示或隐藏元素
    • 如果要频繁的切换元素的显示状态,用 v-show 性能更好
  2. v-if 的原理是:每次动态创建或移除元素,来实现元素的显示或隐藏
    • 如果刚进入页面的时候,某些元素不需要显示出来,而且后期这个元素很可能也不需要显示出来,此时 v-if 性能更好

# 配套指令

ve-else-if 指令,充当 v-if 的 “else-if” 块,可以连续使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="app">
<p v-if="flag">使用v-if的p</p>
<p v-show="flag">使用v-show的p</p>
<p v-if="type === 'A'">A</p>
<p v-if="type === 'B'">B</p>
<p v-if="type === 'C'">C</p>
<p v-else>D</p>
</div>

<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
flag: true,
type: 'C'
}
})
</script>
</body>

** 注意 : **v-else-if 指令必须配合 v-if 指令一起使用,否则不会被识别

# 列表渲染指令

vue 提供了 v-for 列表渲染指令,基于一个数组来循环渲染一个列表结构. v-for 指令需要用 item in items 形式的特殊语法

  • items 是待循环的数组
  • item 是被循环的每一项

v-for 指令还支持一个可选的第二个参数,即当前项的索引。语法格式为 (item, index) in list

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
<tr v-for="(item, index) in list">
<td>{{index}}</td>
<td>{{item.employeeId}}</td>
<td>{{item.firstName}}</td>
<td>{{item.lastName}}</td>
<td>{{item.email}}</td>
<td>{{item.phoneNumber}}</td>
<td>{{item.hireDate}}</td>
<td>{{item.jobId}}</td>
<td>{{item.salary}}</td>
<td>{{item.commissionPct}}</td>
<td>{{item.managerId}}</td>
<td>{{item.departmentId}}</td>
</tr>

data: {
list: [
{
"employeeId": 100,
"firstName": "Steven",
"lastName": "King",
"email": "SKING",
"phoneNumber": "515.123.4567",
"hireDate": "1987-06-16T15:00:00.000+00:00",
"jobId": "AD_PRES",
"salary": 24000,
"commissionPct": null,
"managerId": null,
"departmentId": 90
},
{
"employeeId": 101,
"firstName": "Neena",
"lastName": "Kochhar",
"email": "NKOCHHAR",
"phoneNumber": "515.123.4568",
"hireDate": "1989-09-20T16:00:00.000+00:00",
"jobId": "AD_VP",
"salary": 17000,
"commissionPct": null,
"managerId": 100,
"departmentId": 90
}
}
]
}

** 注意 : **

  • v-for 指令中的 item 项和 index 索引都是形参,可以根据需要重命名
  • 官方建议,只要用到了 v-for 指令,那么一定要绑定一个 :key 属性,而且尽量把 id 作为 key 的值
  • key 的值只能是字符串或数字类型
  • key 的值必须具有唯一性
  • 使用 index 的值当作 key 的值没有任何意义 (因为 index 的值不具有唯一性)
  • 建议使用 v-for 指令时一定要指定 key 的值 (既提升性能,又防止列表状态紊乱)

# 过滤器

过滤器 (Filters) 是 vue 提供的功能,常用于文本的格式化。过滤器可以用在两个地方:插值表达式和 v-bind 属性绑定

过滤器应该可以被添加在 JavaScript 表达式的尾部,由 “管道符” 进行调用

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
<body>
<div id="app">
<p>message的值是 : {{message | myFilter}}</p>
<p :class="lowerClass | toLower">hello</p>
</div>

<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'hello vue.js',
lowerClass: 'HHH'
},
filters: {
myFilter(val) {
const regUpper = /\b\w/g
return val.replaceAll(regUpper, match => match.toUpperCase())
},

toLower(val) {
return val.toLowerCase()
}
}
})
</script>
</body>

# 私有过滤器和全局过滤器

在 filters 节点下定义的过滤器,称为 “私有过滤器”, 因为它只能在 vm 实例控制的 el 区域内使用,如果希望在多个 vue 实例之间共享过滤器,则可以定义全局过滤器

1
2
3
Vue.filter('过滤器名称', (str) => {
return str.charAt(0).toUpperCase() + str.slice() + '~~'
})
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
<body>
<div id="app">
<p>message的值是 : {{message | myFilter}}</p>
<p :class="lowerClass | toLower">hello</p>
</div>

<div id="app2">
<!-- 在el: '#app'中定义的过滤器不能在el: '#app2'中使用-->
<p>message的值是 : {{message | myFilter}}</p>
<!-- 在全局中定义的filter可以被多个vue实例所使用-->
<p :class="lowerClass | toLower">hello</p>
</div>

<script src="./lib/vue-2.6.12.js"></script>
<script>
Vue.filter('toLower', val => val.toLowerCase())
const vm = new Vue({
el: '#app',
data: {
message: 'hello vue.js',
lowerClass: 'HHH'
},
filters: {
myFilter(val) {
const regUpper = /\b\w/g
return val.replaceAll(regUpper, match => match.toUpperCase())
}
}
})

const vm2 = new Vue({
el: '#app2',
data: {
lowerClass: 'HHH'
}
})
</script>
</body>

# 连续调用多个过滤器

过滤器可以串联地进行调用

1
2
3
4
<!--    把message的值,交给filterA进行处理-->
<!-- 把filterA处理的记过, 再叫个filterB进行处理-->
<!-- 最终把filterB处理的结果, 作为最终的值渲染到页面上-->
{{message | filterA | filterB}}

过滤器处理函数的形参列表中 :

  • 第一个参数:永远都是 “管道符” 前面待处理的字符串
  • 从第二个参数开始,才是调用过滤器时传来的 arg1 和 arg2 参数