返回

Vue 2.x 中有趣的小故障:点击一次触发多个事件

前端

在探索 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 的事件处理函数推迟执行。我们可以将 box1Clickbox2Click 处理函数修改为:

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 中事件处理函数的执行机制,我们可以避免这样的故障并编写更健壮的应用程序。