Vue 2.x 中有趣的小故障:点击一次触发多个事件
2023-10-21 13:33:13
在探索 Vue.js 3 的源码时,我偶然发现了一个存在于 Vue 2.x 版本中的有趣小故障。尽管这并不是 Vue 3 的内容,但它足够有趣,所以我决定在此记录下来。
从表面上看,这个小故障是当点击一个元素时,其自身的点击事件和父元素的点击事件都会被触发。乍一看似乎违反了事件冒泡的常规行为,其中事件从子元素冒泡到父元素。
探索小故障
为了演示这个小故障,请考虑以下 HTML 和 JavaScript 代码:
<div id="box1" @click="box1Click">Box 1</div>
<div id="box2" @click="box2Click">Box 2</div>
new Vue({
el: '#app',
methods: {
box1Click() {
console.log('Box 1 clicked');
},
box2Click() {
console.log('Box 2 clicked');
},
},
});
当我们点击 #box1
时,两个 console.log
语句都会被打印出来:
Box 1 clicked
Box 2 clicked
这个行为令人惊讶,因为我们期望只有 #box1
的点击事件被触发。
事件冒泡的原理
在事件冒泡中,当一个事件发生在子元素上时,它会逐级向上传播到父元素。每个元素都可以选择处理事件或让它继续冒泡。
在上面的例子中,#box1
的点击事件会首先触发 #box1
上的事件处理函数。然后,它会冒泡到 #box2
,并触发 #box2
上的事件处理函数。
故障的原因
造成这个故障的原因在于 Vue.js 2.x 中事件处理函数调用的时机。在 Vue 2.x 中,当一个事件触发时,它的处理函数会被立即调用。这与 Vue 3 不同,在 Vue 3 中,事件处理函数会被推迟到稍后执行。
在我们的例子中,当 #box1
的点击事件触发时,box1Click
处理函数会被立即调用。然后,当事件冒泡到 #box2
时,box2Click
处理函数也会被立即调用。这就是为什么这两个处理函数都会被触发的。
解决方案
为了解决这个小故障,我们可以使用 Vue 3 的事件处理函数推迟执行。我们可以将 box1Click
和 box2Click
处理函数修改为:
box1Click() {
this.$nextTick(() => {
console.log('Box 1 clicked');
});
},
box2Click() {
this.$nextTick(() => {
console.log('Box 2 clicked');
});
},
使用 $nextTick
会将处理函数推迟到下一个 tick 执行,从而确保 #box2
的处理函数在 #box1
的处理函数之后执行。
总结
这个 Vue.js 2.x 中的小故障突出了事件处理函数调用时机的微妙之处。通过理解事件冒泡的原理和 Vue.js 2.x 中事件处理函数的执行机制,我们可以避免这样的故障并编写更健壮的应用程序。