有条件地将事件侦听器和处理程序附加到 Vue 组件

bjo*_*and 4 event-handling vue.js

我有一个 Vue 组件,在我的应用程序中的多个地方使用。在某些情况下,我需要使用特定函数处理单击事件,例如:

<div @click="checkNav" />
Run Code Online (Sandbox Code Playgroud)

但是,我只想在需要时附加此处理程序,因此在不需要时它不会不必要地触发。

我尝试将 prop 传递给组件并有条件地附加处理程序,如下所示:

<div @click="isCheckNeeded ? checkNav : null" />
Run Code Online (Sandbox Code Playgroud)

然后在 props 中,我指定了:

isCheckNeeded {
  type: Boolean,
  required: false,
  default: false,
}
Run Code Online (Sandbox Code Playgroud)

然而,我的checkNav函数永远不会触发,并且我已经isCheckNeeded在 Vue devtools 中仔细检查了这一点。

这种条件检查是否不可能或不推荐?是否有更好的方法来有条件地附加事件侦听器/处理程序?

ton*_*y19 5

查看模板的编译方式可能有助于了解问题的原因......

v-on接收到方法名称时,vue-template-compiler将其编译为方法查找,其中解析的方法成为事件处理程序[1]。例如,您的模板<div @click="checkNav" />被编译到此渲染函数中:

with (this) {
  return _c("div", { on: { click: checkNav } })
}
Run Code Online (Sandbox Code Playgroud)

另一方面,使用内联事件处理程序<div @click="isCheckNeeded ? checkNav : null" />编译为:

with (this) {
  return _c("div", {
    on: {
      click: function ($event) {
        isCheckNeeded ? checkNav : null
      },
    },
  })
}
Run Code Online (Sandbox Code Playgroud)

请注意这里的几件事:

  1. 该表达式被包装在一个匿名函数中,该函数成为事件处理程序。
  2. 表达式的结果是方法名称(而不是方法调用)或null。评估方法名称实际上是一个无操作。

解决方案一:将方法名改为方法调用

这可能是最简单的解决方案,但它的缺点是处理程序总是在单击时被调用(尽管错误isCheckNeeded会导致提前返回)。

<!-- BEFORE: -->
<!--
<div @click="isCheckNeeded ? checkNav : null" />
-->

<!-- AFTER: -->
<div @click="isCheckNeeded ? checkNav() : null" />

<!-- OR: -->
<div @click="isCheckNeeded && checkNav()" />
Run Code Online (Sandbox Code Playgroud)

解决方案2:使用动态事件

这稍微复杂一些,但它的优点是仅在必要时注册事件处理程序。isCheckNeeded当为 false时,事件处理程序会自动取消注册。

<div @[clickEvent]="checkNav" />
...
<script>
export default {
  computed: {
    clickEvent() {
      return this.isCheckNeeded ? 'click' : null
    }
  },
}
</script>
Run Code Online (Sandbox Code Playgroud)

Vue.component('my-component', {
  template: `<div @[clickEvent]="checkNav"><slot/></div>`,
  props: {
    isCheckNeeded: Boolean
  },
  computed: {
    clickEvent() {
      return this.isCheckNeeded ? 'click' : null
    }
  },
  methods: {
    checkNav() {
      console.log('checkNav')
    }
  }
})

new Vue({
  el: '#app',
  data() {
    return {
      isCheckNeeded: false
    }
  }
})
Run Code Online (Sandbox Code Playgroud)
.click-area {
  border: solid 1px;
  padding: 2rem;
  margin: 1rem;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@2.6.12"></script>

<div id="app">
  <button @click="isCheckNeeded = !isCheckNeeded">Toggle click handler</button>
  <pre>isCheckNeeded={{isCheckNeeded}}</pre>

  <my-component :is-check-needed="isCheckNeeded">
    <div class="click-area">
      <span v-if="isCheckNeeded">Click me!</span>
      <span v-else>Clicking ignored</span>
    </div>
  </my-component>
</div>
Run Code Online (Sandbox Code Playgroud)