# Vue3教程 - 5 指令与事件修饰符
和 Vue2 基本一样。
下面来介绍一下模板中常用的一些属性指令和事件相关的属性。
# 5.1 指令
指令其实就是 Vue 中的定义的特有属性,通过这些属性,可以让模板中的数据 Vue 中的数据进行绑定,前面也简单的使用了,下面详细介绍一下。
# 1 v-text和v-html
前面在显示文本的时候,使用的是插值表达式,还可以使用 v-text
和 v-html
指令。
举个栗子:
<template>
<!-- v-text会按普通字符串显示 -->
<div v-text="msg"></div>
<!-- v-html则会按照html来渲染显示 -->
<div v-html="msg"></div>
<!-- 使用差值表达式 -->
<div>{{ msg }}</div>
</template>
<!-- setup -->
<script lang="ts" setup>
// 定义变量
let msg = "<h1>Hello Vue</h1>";
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 差值表达式和
v-text
会按普通字符串显示文本,在实际的开发中,使用插值表达式更方便一些。 v-html
则会按照html来渲染显示,所以要确保内容是安全的,自己控制的,否则可能会跨站脚本攻击(XSS)。
# 2 v-bind属性绑定
v-bind
是Vue提供的属性绑定机制。我们想在普通的 html 标签中使用 Vue 中数据,则需要使用 v-bind
属性,将Vue中定义的属性绑定到 DOM 元素上,当数据发生变化,DOM 元素会重新渲染。
举个栗子:
<template>
<!-- v-bind是Vue中,提供的用于绑定属性的指令 -->
<input type="button" value="按钮1" v-bind:title="myTitle" />
<br/>
<!-- v-bind指令可以被简写为 :要绑定的属性 -->
<input type="button" value="按钮2" :title="myTitle" />
<br/>
<!-- v-bind中,可以写合法的JS表达式 -->
<input type="button" value="按钮3" :title="myTitle + 123" />
</template>
<!-- setup -->
<script lang="ts" setup>
// 定义数据
let myTitle = "Hello Doubi";
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在元素上使用 v-bind:属性
,就可以在普通的 html 属性中使用 Vue 定义的数据。
上面为三个按钮定义了 title
属性,当鼠标放在按钮上面,会有提示信息。
v-bind:属性
可以简写成 :属性
,例如 :title
,v-bind
中也可以写 JS 表达式。
一个标签可以绑定多个属性:
<div :a="'valueA'" :b="valueB">Hello</div>
上面 :a="'valueA'"
表示传递的是字符串'valueA'
, :b="valueB"
传递的是 Vue 中定义的数据。
其实还有一种写法,就是通过对象传递:
<div v-bind="{'a': 'valueA', 'b': valueB}">Hello</div>
# 3 v-on事件绑定
v-on:
指令是 Vue 提供的事件绑定机制,用于事件绑定。前面在演示的时候也用过。
使用时候要注意,只能绑定 Vue 中提供的方法,下面的代码就有问题:
<input type="button" value="按钮" v-on:click="alert('hello')">
像上面这样直接写 alert
,是无法在点击按钮的时候弹出的, 因为无法在 Vue 中找到 alert
方法。
可以在 setup 中定义一个方法,例如 show 方法,然后在 html 元素上绑定事件上调用show方法,在 show
方法中 alert
。
举个栗子:
<template>
<input type="button" value="按钮" v-on:click="show('hello world')">
</template>
<!-- setup -->
<script lang="ts" setup>
function show(msg: string) {
alert(msg)
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
v-on:
可以缩写成 @
,例如 @click
。通过 v-on:click
指定处理方法的时候,如果没有参数,可以不写方法后面的 ()
,v-on:click="show"
,如果写了 ()
可以传递参数。
同样还可以绑定其他一些事件,例如 mouseover 等事件:v-on:mouseover
。
当调用函数的时候,没有传递参数,那么第一个参数是 event 事件。
通过 event 可以获取事件的一些信息。
<template>
<input type="button" value="按钮" v-on:click="show">
</template>
<!-- setup -->
<script lang="ts" setup>
function show(event: MouseEvent) {
// 鼠标指针在触发事件时相对于浏览器窗口的水平和垂直坐标
console.log(`鼠标位置:(${event.clientX}, ${event.clientY})`); // 相对于视口
console.log(`页面位置:(${event.pageX}, ${event.pageY})`); // 相对于文档
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
当调用函数的时候,传递了参数,那么需要获取 event 事件,必须显式传递:
<template>
<input type="button" value="按钮" v-on:click="show('Hello', $event)">
</template>
<!-- setup -->
<script lang="ts" setup>
function show(msg: string, event: MouseEvent) {
console.log('msg:', msg);
// 通过事件可以获取一些
// 鼠标指针在触发事件时相对于浏览器窗口的水平和垂直坐标
console.log(`鼠标位置:(${event.clientX}, ${event.clientY})`); // 相对于视口
console.log(`页面位置:(${event.pageX}, ${event.pageY})`); // 相对于文档
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 4 v-model双向数据绑定
v-bind
只能实现数据的单向绑定,将数据绑定到 DOM 元素上,修改数据,DOM自动渲染。但是修改DOM中的值,Vue 中的数据无法同步修改。
v-model 是实现双向数据绑定,双向数据绑定是修改了 Vue 的数据,DOM 元素会重新渲染,修改了 DOM 元素中的值,Vue 中的数据也会发生变化。但是 v-model 只能运用在表单元素中,例如 input、select 等,不能运用在 div、span 中。
举个栗子:
<template>
<h4>{{msg}}</h4>
<!-- 使用v-model指令,可以实现 表单元素和数据的双向数据绑定 -->
<input type="text" v-model="msg">
</template>
<!-- setup -->
<script lang="ts" setup>
import { ref } from 'vue'
let msg = ref("Hello Doubi") // 需要使用响应式数据
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上面的代码,使用了 v-model
在 input
上绑定了 msg
的值,这样在修改文本中的数据的时候,msg
的值就会发生变化,则 <h4>
中的内容就会同步更新。修改 msg
的值,input
中的值也会发送变化。
注意:v-model
只能运用在表单元素中。
# 5 v-for遍历
使用v-for指令可以遍历数组、对象等。
下面列举了几种遍历的使用场景:
<template>
<div>
<!-- 1、遍历数组,第二个参数i如果不用可以省略 -->
<p v-for="(item, i) in list" :key="item">索引值:{{ i }} --- 每一项:{{ item }}</p>
</div>
<div>
<!-- 2、遍历对象数组 -->
<p v-for="(user, i) in userList" :key="user.id">Id:{{ user.id }} --- 名字:{{ user.name }} --- 索引:{{ i }}</p>
</div>
<div>
<!-- 3、循环遍历对象身上的属性 -->
<!-- 注意:在遍历对象身上的键值对的时候, 除了有val、key,在第三个位置还有一个索引-->
<p v-for="(value, key, i) in user" :key="key">key: {{ key }} --- value: {{ value }} --- 索引值: {{ i }}</p>
</div>
<div>
<!-- 4、遍历数字,in还可以放数字 -->
<!-- 注意:如果使用 v-for 迭代数字的话,前面的 count 值从 1 开始 -->
<p v-for="count in 5" :key="count">这是第 {{ count }} 次循环</p>
</div>
</template>
<!-- setup -->
<script lang="ts" setup>
import { reactive } from 'vue'
// 定义响应式数据
// 定义数组
let list = reactive([1, 2, 3, 4])
//定义数组对象
let userList = reactive([
{ id: 1, name: '逗比' },
{ id: 2, name: '牛比' },
{ id: 3, name: '二比' }
])
// 定义对象
let user = reactive({
id: 1,
name: '逗比',
gender: '男'
})
</script>
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
使用时需要注意,需要为每一个选项使用 :key
来指定唯一的 key 值。如果不使用 key
属性,Vue 可能会错误地复用或重新排序节点,从而导致意想不到的渲染问题。使用 key
属性可以确保每个节点都有唯一的标识,从而避免这些问题。
显示效果:
如果要修改列表显示,只需要修改数据就可以了:
<template>
<div>
<!-- 遍历对象数组 -->
<p v-for="(user, i) in userList" key="user.id">Id:{{ user.id }} --- 名字:{{ user.name }} --- 索引:{{ i }}</p>
<button @click="changeData">改变数据</button>
</div>
</template>
<!-- setup -->
<script lang="ts" setup>
import { reactive } from 'vue'
// 定义响应式数据
let userList = reactive([
{ id: 1, name: '逗比' },
{ id: 2, name: '牛比' },
{ id: 3, name: '二比' }
])
function changeData() { // 改变数据
userList[2].name = "菜比"
}
</script>
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
通过改变响应式数据,页面会自动渲染,不需要操作 DOM,非常的方便。
# 6 v-if和v-show判断显示
v-if
和 v-show
指令都是用来控制组件的显示和隐藏的。
有如下代码:
下面的代码实现的效果是:当点击按钮时候,调用方法修改了 flag 的值,两个 h3 使用 v-if 和 v-show 指令通过flag来控制元素的显示和隐藏。
<template>
<input type="button" value="toggle" @click="toggle">
<h3 v-if="flag">这是用v-if控制的元素</h3>
<h3 v-show="flag">这是用v-show控制的元素</h3>
</template>
<!-- setup -->
<script lang="ts" setup>
import { ref } from 'vue'
// 定义响应式数据
let flag = ref(false)
function toggle() {
flag.value = !flag.value
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
那么v-if和v-show有什么区别?
v-if
的特点:每次都会重新删除或创建元素;v-show
的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的display:none
样式。v-if
有较高的切换性能消耗,如果元素涉及到频繁的切换,最好不要使用v-if
, 而是推荐使用v-show
;v-show
有较高的初始渲染消耗,如果元素可能永远也不会被显示出来被用户看到,则推荐使用v-if
;
# 7 v-if和v-else
什么是条件判断语句,就不需要我说明了吧,以下两个属性!
v-if
v-else
举个栗子:
<template>
<h1 v-if="type">Yes</h1>
<h1 v-else>No</h1>
<button @click="switchShow">切换</button>
</template>
<!-- setup -->
<script lang="ts" setup>
import { ref } from 'vue';
// 定义响应式数据
let type = ref(true)
function switchShow() {
type.value = !type.value
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在上面的代码中,通过 type 的值来控制两个 <h1>
的切换显示,并通过改变 type
的值来实现切换。
注意:两个带 v-if
和 v-else
的元素需要相邻。
除了 v-if
和 v-else
, 还有 v-else-if
可以使用。
举个栗子:
<template>
<h1 v-if="type == 0">A</h1>
<h1 v-else-if="type == 1">B</h1>
<h1 v-else-if="type == 2">C</h1>
<h1 v-else>D</h1>
<button @click="switchShow">切换</button>
</template>
<!-- setup -->
<script lang="ts" setup>
import { ref } from 'vue';
// 定义响应式数据
let type = ref(0)
function switchShow() {
type.value++
if (type.value > 3) {
type.value = 0
}
}
</script>
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
上面使用 type 控制多个 <h1>
的切换显示。
# 5.2 事件修饰符
我们可以使用 v-on
指令绑定事件。下面介绍一下事件修饰符。
# 1 +.stop
阻止冒泡。
<template>
<!-- 使用 .stop 阻止冒泡 -->
<div @click="div1Handler">
<button @click.stop="btnHandler">点我</button>
</div>
</template>
<!-- setup -->
<script lang="ts" setup>
function div1Handler() {
console.log("点击了div")
}
function btnHandler() {
console.log("点击了button")
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
如上面的代码,在一个div中有一个按钮,同时给div和按钮添加了点击事件,当点击按钮的时候,会先触发按钮的点击事件,然后再触发div的点击事件,事件是从内部元素向上冒泡的,如果想阻止触发div的点击事件,可以给按钮的点击事件添加 .stop
修饰符,阻止事件冒泡,这样就只会触发按钮的点击事件了。
# 2 +.prevent
阻止默认事件。
<template>
<!-- 使用 .prevent 阻止默认行为 -->
<a href="http://www.baidu.com" @click.prevent="clickLink">有问题,先百度</a>
</template>
<!-- setup -->
<script lang="ts" setup>
function clickLink() {
console.log("点击了链接")
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
在上面的代码中有一个 a标签
,a标签
有一个默认事件是跳转到百度,如果想阻止跳转到百度,可以添加一个点击事件,并添加 .prevent
修饰符阻止 a标签
的默认事件。这样点击 a标签
只会调用点击事件的方法, 这样在方法中处理我们的逻辑就可以了。
使用同样的方式,也可以阻止表单的默认事件等。
# 3 +.capture
添加事件侦听器时使用事件捕获模式。
<template>
<!-- 使用 .capture 实现捕获触发事件的机制 -->
<div @click.capture="div1Handler">
<input type="button" value="点我" @click.stop="btnHandler">
</div>
</template>
<!-- setup -->
<script lang="ts" setup>
function div1Handler() {
console.log("点击了div")
}
function btnHandler() {
console.log("点击了button")
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
事件的默认传递是冒泡方式的,是从内部往外部传递的。如果想事件由外向内传递,可以使用 .capture
修饰符来实现捕获触发事件。在上面的代码中,在外部的div的点击事件上添加了.capture
修饰符,那么会先触发div的点击事件。
# 4 +.self
只当事件在该元素本身(比如不是子元素)触发时触发回调。
<template>
<!-- 使用 .self 实现只有点击当前元素时候,才会触发事件处理函数 -->
<div class="inner" @click.self="div1Handler">
<input type="button" value="点我" @click="btnHandler">
</div>
</template>
2
3
4
5
6
7
8
在外部div元素的事件上添加 .self
修饰符后,只有在点击div本身才会触发div的事件,点击其中的元素不会触发div的事件。
.self
不能阻止内部事件的冒泡,当有多层元素嵌套时,点击了内部元素,本层元素添加了.self
修饰,则本层元素不会接受到事件,但是事件会继续冒泡到更外部元素。
.stop
是点击内部元素时,阻止事件向上传递,效果是点击外部元素本身才会触发外部元素的事件。
.self
也是点击外部元素本身才会触发外部元素的事件,那么 .stop
和 .self
什么区别呢?
.stop
是作用在子元素上的,只会阻止某一个子元素的事件不向上传递;而 .self
是作用在外部元素上的,那么内部所有元素的事件都不会触发外部元素的事件,只监听自身元素的事件。
# 5 +.once
事件只触发一次。
<template>
<!-- 使用 .once 只触发一次事件处理函数 -->
<a href="http://www.baidu.com" @click.prevent.once="clickLink">有问题,先百度</a>
</template>
2
3
4
5
6
使用 .once
只会触发一次函数处理,修饰符可以串联起来使用。
像上面的代码只会在点击 a标签
的第一次阻止默认事件,当再次点击 a标签
就不会阻止默认事件了,而是会跳转到百度。