# Vue3教程 - 5 指令与事件修饰符

和 Vue2 基本一样。

下面来介绍一下模板中常用的一些属性指令和事件相关的属性。


# 5.1 指令

指令其实就是 Vue 中的定义的特有属性,通过这些属性,可以让模板中的数据 Vue 中的数据进行绑定,前面也简单的使用了,下面详细介绍一下。

# 1 v-text和v-html

前面在显示文本的时候,使用的是插值表达式,还可以使用 v-textv-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>
1
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>
1
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:属性 可以简写成 :属性 ,例如 :titlev-bind 中也可以写 JS 表达式。

一个标签可以绑定多个属性:

<div :a="'valueA'" :b="valueB">Hello</div>
1

上面 :a="'valueA'" 表示传递的是字符串'valueA':b="valueB" 传递的是 Vue 中定义的数据。

其实还有一种写法,就是通过对象传递:

<div v-bind="{'a': 'valueA', 'b': valueB}">Hello</div>
1

# 3 v-on事件绑定

v-on: 指令是 Vue 提供的事件绑定机制,用于事件绑定。前面在演示的时候也用过。

使用时候要注意,只能绑定 Vue 中提供的方法,下面的代码就有问题:

<input type="button" value="按钮" v-on:click="alert('hello')">
1

像上面这样直接写 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>
1
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>
1
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>
1
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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

上面的代码,使用了 v-modelinput 上绑定了 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>
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

使用时需要注意,需要为每一个选项使用 :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>
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

通过改变响应式数据,页面会自动渲染,不需要操作 DOM,非常的方便。

# 6 v-if和v-show判断显示

v-ifv-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>
1
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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

在上面的代码中,通过 type 的值来控制两个 <h1> 的切换显示,并通过改变 type 的值来实现切换。

注意:两个带 v-ifv-else 的元素需要相邻。

除了 v-ifv-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>
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

上面使用 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>
1
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>
1
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>
1
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>
1
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>
1
2
3
4
5
6

使用 .once 只会触发一次函数处理,修饰符可以串联起来使用。

像上面的代码只会在点击 a标签 的第一次阻止默认事件,当再次点击 a标签 就不会阻止默认事件了,而是会跳转到百度。