通过 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: { addCount(e) { 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>
<div @click.self="doThat">...</div>
<a @click.once="doThis"></a>
<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) { 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修饰符,阻止了事件冒泡。