Vue3-03

通过 Key 管理状态

Vue 为了尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这样可以使 Vue 变得非常快。不过,这也意味着,你应该避免在模板中,通过 v-if 来切换元素的显示状态。

Key 是 Vue 用来追踪节点的一个特殊的属性,它可以用于强制替换元素/组件而不是重复使用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<button @click="toggle">Toggle</button>
<div v-if="show" :key="show">show</div>
</div>
</template>

<script>
export default {
data() {
return {
show: true,
};
},
methods: {
toggle() {
this.show = !this.show;
},
},
};
</script>

提示
key 在这里是通过 v-bind 动态绑定的,因为静态的 key 会被作为一个属性传递给被渲染的元素,而动态绑定的 key 会基于每次更新的节点进行更新。推荐使用 v-bind 来绑定 key,因为它能提供更好的性能,同时也更符合 Vue 的风格指南。在任何可行的时候为 v-for 设置 key,除非迭代 DOM 内容足够简单,或者是刻意依赖默认行为以获取性能提升。

Key 的来源

Key 的值必须是唯一的,它可以是任何类型的值,但在不同的节点之间应该使用不同的值。

不要用索引作为 key,除非你的列表是静态的,且项目永远不会被重新排序或过滤。使用不唯一的值可能会导致状态错误或渲染错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div v-for="item in items" :key="item.id">
{{ item.name }}
</div>
</template>

<script>
export default {
data() {
return {
items: [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
{ id: 3, name: 'baz' },
],
};
},
};
</script>

可以看到,我们使用了每个 item 的 id 作为 key,这样就可以保证每个 key 都是唯一的。

事件处理

v-on 简写 @

Vue 事件处理方法使用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

1
2
<button v-on:click="counter += 1">增加 1</button>
<button @click="say('what')">Say what</button>

事件处理器可以是一个内联语句,或者是一个方法名。

内联事件处理器

通常用于处理非常简单的事件处理器。

在 components/EventDemo.vue 中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<button @click="counter += 1">增加 1</button>
<button @click="say('what')">Say what</button>
<p>counter: {{ counter }}</p>
</div>
</template>

<script>
export default {
data() {
return {
counter: 0,
};
},
methods: {
say(msg) {
alert(msg);
},
},
};
</script>

方法事件处理器

通常用于处理复杂的事件处理器。

创建 EventDemo2.vue,添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<button @click="add(1)">增加 1</button>
<button @click="add(2)">增加 2</button>
<p>counter: {{ counter }}</p>
</div>
</template>

<script>
export default {
data() {
return {
counter: 0,
};
},
methods: {
add(num) {
this.counter += num;
},
},
};
</script>

以上代码中,我们通过methods定义了一个add方法,然后在模板中通过@click="add(1)"调用该方法。

事件传参

在上面的例子中,我们通过@click="add(1)"调用add方法,这样就可以实现增加1的功能。但是如果我们想要实现增加2的功能,就需要再定义一个add2方法,这样就会导致代码冗余。

我们可以通过事件传参的方式来解决这个问题。

方法事件处理器中的add方法就是一个事件传参的例子。

下面这个例子是一个更加复杂的事件传参的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div>
<button @click="addzCount">增加 1</button>
<p>counter: {{ counter }}</p>
</div>
</template>

<script>
export default {
data() {
return {
counter: 0,
};
},
methods: {
// event 为事件对象
addCount(e) {
// 在 vue 中的 event 对象是原生 event 对象的包装
e.target.innerText = '增加 1';
this.counter += 1;
}
}
}
</script>

另一个事件传参的例子

这个例子演示了 $event 传参的用法。
$event 是 Vue 提供的一个特殊变量,它是原生 event 对象的包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<p @click="getNameHandler(item, $event)" v-for="(item, index) in names" :key="index">
{{ item }}
</p>
</div>
</template>

<script>
export default {
data() {
return {
names: ['foo', 'bar', 'baz'],
};
},
methods: {
getNameHandler(name) {
alert(name);
console.log(name);
},
},
};
</script>

事件修饰符

在处理事件时调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

  • .stop

  • .prevent

  • .capture

  • .self

  • .once

  • .passive

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
<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form @submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div @click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div @click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a @click.once="doThis"></a>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div @scroll.passive="onScroll">...</div>

官方文档:事件修饰符

阻止默认行为

“默认行为”即事件的默认行为,比如点击a标签,会跳转到a标签的href属性指定的链接。

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<h3>事件修饰符</h3>
<a @click.prevent="clickHandle" href="yuzhii0718.github.io">Yuzhii's Blog
</a>
</template>

<script>
export default {
methods: {
clickHandle(e) {
// 阻止默认行为
// e.preventDefault();
// 将html中的a标签的 @click改为 @click.prevent 可以阻止默认行为
alert('click');
},
},
};
</script>

阻止事件冒泡

“冒泡”即事件的传播,当一个元素上的事件被触发后,该事件会向父元素传播,直到根元素。

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<h3>事件修饰符</h3>
<div @click="divClick">
<button @click.stop="btnClick">按钮</button>
</div>

<div @click="divClick">
<button @click="btnClick">按钮</button>
</div>
</template>

<script>
export default {
methods: {
divClick() {
alert('div click');
},
btnClick() {
alert('btn click');
},
},
};
</script>

两个div中的按钮,点击第一个按钮,只会触发第一个div的点击事件,不会触发第二个div的点击事件。

因为第一个按钮的点击事件使用了.stop修饰符,阻止了事件冒泡。