Vue v-on:click 对组件不起作用

IT技术 javascript vue-component vuejs2 vue.js
2021-01-13 21:11:27

我正在尝试在组件内使用 on click 指令,但它似乎不起作用。当我单击组件时,当我应该在控制台中单击“测试”时,什么也没有发生。我在控制台中没有看到任何错误,所以我不知道我做错了什么。

索引.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vuetest</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

应用程序

<template>
  <div id="app">
    <test v-on:click="testFunction"></test>
  </div>
</template>

<script>
import Test from './components/Test'

export default {
  name: 'app',
  methods: {
    testFunction: function (event) {
      console.log('test clicked')
    }
  },
  components: {
    Test
  }
}
</script>

Test.vue(组件)

<template>
  <div>
    click here
  </div>
</template>

<script>
export default {
  name: 'test',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>
6个回答

如果要在组件的根元素上侦听本机事件,则必须使用.native修饰符 for v-on,如下所示:

<template>
  <div id="app">
    <test v-on:click.native="testFunction"></test>
  </div>
</template>

或者简而言之,如评论中所建议的,您也可以这样做:

<template>
  <div id="app">
    <test @click.native="testFunction"></test>
  </div>
</template>

参考阅读有关本机事件的更多信息

不建议在 vue 中使用 native 修饰符。仅在绝对必要时才使用它。请参阅@jim-mcneely 以获取实现此目的的正确方法,即从子元素发出事件,然后在父元素中恢复它。
2021-03-21 21:11:27
2021-03-29 21:11:27
或者简写 @click.native="testFunction"
2021-04-07 21:11:27
什么是本地事件或它与其他正常事件有何不同为什么是根元素的这种特殊情况???
2021-04-11 21:11:27
这是本地事件的正确链接
2021-04-12 21:11:27

我认为该$emit功能更适合我认为您的要求。它使您的组件与 Vue 实例分离,以便它可以在许多上下文中重用。

// Child component
<template>
  <div id="app">
    <test @click="$emit('test-click')"></test>
  </div>
</template>

在 HTML 中使用它

// Parent component
<test @test-click="testFunction">
.native修饰符相比,这绝对是更明确的解决方案,并使父组件更少依赖子组件的实现细节,这几乎总是一个优点。
2021-03-14 21:11:27
我有点困惑。$emit 部分是否应该在测试组件模板中?
2021-03-19 21:11:27
@NelsonRodriguez 所说的话。使用@click="$emit('click')".
2021-03-30 21:11:27
我相信这是正确的答案。在组件内处理事件的链接。不要关心要调用的单击事件“版本”的父组件。我实际上在组件中将它实现为下面的答案@click="$emit('click')",这样父组件只使用常规@click
2021-04-05 21:11:27

这是@Neps 的答案,但有详细信息。


注意如果您不想修改组件或无法访问它,@Saurabh 的答案更合适。


为什么@click 不能正常工作?

组件很复杂。一个组件可以是一个漂亮的小按钮包装器,另一个可以是一个包含一堆逻辑的整个表格。Vue 不知道你在绑定v-model或使用时究竟期望什么,v-on所以所有这些都应该由组件的创建者处理。

如何处理点击事件

根据Vue 文档$emit将事件传递给父级。来自文档的示例:

主文件

<blog-post
  @enlarge-text="onEnlargeText"
/>

零件

<button @click="$emit('enlarge-text')">
  Enlarge text
</button>

@v-on 简写

组件处理本机click事件并发出父级事件@enlarge-text="..."

enlarge-text可以替换click为使其看起来像我们正在处理本机点击事件:

<blog-post
  @click="onEnlargeText"
></blog-post>
<button @click="$emit('click')">
  Enlarge text
</button>

但这还不是全部。$emit允许通过事件传递特定值。在 native 的情况下click,值为MouseEvent(与 Vue 无关的 JS 事件)。

Vue 将该事件存储在一个$event变量中。因此,最好$event使用事件发出以创建原生事件使用的印象:

<button v-on:click="$emit('click', $event)">
  Enlarge text
</button>

正如 Chris Fritz(Vue.js Core Team Emeriti)在VueCONF US 2019提到的

如果我们让 Kia 进入.native,然后基本输入的根元素突然从输入变成了标签,这个组件就坏了,这并不明显,事实上,除非你有一个非常好的测试,否则你甚至可能不会马上发现它。相反,通过避免使用我目前认为将在 Vue 3 中删除的反模式.native修饰符您将能够明确定义父级可能关心将哪些元素侦听器添加到...

使用 Vue 2

使用$listeners

因此,如果您使用 Vue 2,解决此问题的更好选择是使用完全透明的包装器逻辑。为此,Vue 提供了一个$listeners属性,其中包含在组件上使用的侦听器对象。例如:

{
  focus: function (event) { /* ... */ }
  input: function (value) { /* ... */ },
}

然后我们只需要添加v-on="$listeners"test组件中,例如:

Test.vue(子组件)

<template>
  <div v-on="$listeners">
    click here
  </div>
</template>

现在该<test>组件是一个完全透明的包装器,这意味着它可以像普通<div>元素一样使用:所有侦听器都可以工作,无需.native修饰符。

演示:

Vue.component('test', {
  template: `
    <div class="child" v-on="$listeners">
      Click here
    </div>`
})

new Vue({
  el: "#myApp",
  data: {},
  methods: {
    testFunction: function(event) {
      console.log('test clicked')
    }
  }
})
div.child{border:5px dotted orange; padding:20px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
  <test @click="testFunction"></test>
</div>

使用$emit方法:

我们也可以$emit为此使用方法,这有助于我们监听父组件中的子组件事件。为此,我们首先需要从子组件发出一个自定义事件,例如:

Test.vue(子组件)

<test @click="$emit('my-event')"></test>

重要提示:事件名称始终使用 kebab-case。有关这一点的更多信息和演示,请查看这个答案:VueJS将计算值从组件传递到父级

现在,我们只需要在父组件中监听这个发出的自定义事件,例如:

应用程序

<test @my-event="testFunction"></test>

因此,基本上我们将简单地使用代替v-on:click或 简写@clickv-on:my-event@my-event

演示:

Vue.component('test', {
  template: `
    <div class="child" @click="$emit('my-event')">
      Click here
    </div>`
})

new Vue({
  el: "#myApp",
  data: {},
  methods: {
    testFunction: function(event) {
      console.log('test clicked')
    }
  }
})
div.child{border:5px dotted orange; padding:20px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
  <test @my-event="testFunction"></test>
</div>


使用 Vue 3

使用v-bind="$attrs"

Vue 3 将在很多方面让我们的生活变得更轻松。它的一个例子是,它将帮助我们创建一个更简单的透明包装器,只需使用v-bind="$attrs". 通过在子组件上使用它,我们的侦听器不仅可以直接从父组件工作,而且任何其他属性也可以像正常一样工作<div>

所以,关于这个问题,我们不需要在 Vue 3 中更新任何东西,你的代码仍然可以正常工作,就像<div>这里的根元素一样,它会自动监听所有子事件。

演示 #1:

const { createApp } = Vue;

const Test = {
  template: `
    <div class="child">
      Click here
    </div>`
};

const App = {
  components: { Test },
  setup() {
    const testFunction = event => {
      console.log("test clicked");
    };
    return { testFunction };
  }
};

createApp(App).mount("#myApp");
div.child{border:5px dotted orange; padding:20px;}
<script src="//unpkg.com/vue@next"></script>
<div id="myApp">
  <test v-on:click="testFunction"></test>
</div>

但是对于具有嵌套元素的复杂组件,我们需要将属性和事件应用于 main<input />而不是父标签,我们可以简单地使用v-bind="$attrs"

演示 #2:

const { createApp } = Vue;

const BaseInput = {
  props: ['label', 'value'],
  template: `
    <label>
      {{ label }}
      <input v-bind="$attrs">
    </label>`
};

const App = {
  components: { BaseInput },
  setup() {
    const search = event => {
      console.clear();
      console.log("Searching...", event.target.value);
    };
    return { search };
  }
};

createApp(App).mount("#myApp");
input{padding:8px;}
<script src="//unpkg.com/vue@next"></script>
<div id="myApp">
  <base-input 
    label="Search: "
    placeholder="Search"
    @keyup="search">
  </base-input><br/>
</div>

有点冗长,但这就是我的做法:

@click="$emit('click', $event)"

更新: @sparkyspider 添加的示例

<div-container @click="doSomething"></div-container>

div-container组件...

<template>
  <div @click="$emit('click', $event);">The inner div</div>
</template>
我将其更改为$emit但什么也没发生。我还需要做些什么$emit吗? jsfiddle.net/xwvhy6a3
2021-03-19 21:11:27
这是去哪里?你为什么把它放在那里?请为查看此答案的人添加更多详细信息,好吗?
2021-04-01 21:11:27
@RichardBarraclough 您的组件现在发出您的自定义事件“clickTreeItem”。接下来是处理在使用该组件时如何处理该事件: v-on:myEvent="myMethod"
2021-04-06 21:11:27
在这个例子中,这将被放置在 Test.vue 组件中的 div 标签上。然后可以在 App.vue 中使用组件测试时使用 v-on:click="testFunction" 或 @click="testFunction"
2021-04-11 21:11:27