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

index . 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>

App.vue

<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>

测试。Vue(组件)

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

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

当前回答

这是@Neps的答案,但有细节。


注意:如果你不想修改你的组件或者没有权限访问它,@Saurabh的答案更适合。


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

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

如何处理点击事件

根据Vue文档,$emit将事件传递给父节点。来自docs的例子:

主文件

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

组件

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

(@是v-on的缩写)

组件处理本机单击事件并发出父元素的@enlarge-text="…"

放大文本可以替换为点击,使它看起来像我们在处理一个本地点击事件:

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

但这还不是全部。$emit允许将特定值与事件一起传递。在本机单击的情况下,值为MouseEvent(与Vue无关的JS事件)。

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

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

其他回答

如果你想监听组件根元素上的本机事件,你必须使用.native修饰符来代替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>

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

我认为$emit函数更适合你的要求。它将组件与Vue实例分离,以便在许多上下文中可重用。

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

在HTML中使用

// Parent component
<test @test-click="testFunction">

App.vue

<div id="app">
    <test @itemClicked="testFunction($event)"/>
</div>

Test.vue

<div @click="$emit('itemClicked', data)">
     click here
</div>

正如Chris Fritz (Vue.js核心团队Emeriti)在VueCONF US 2019中提到的那样

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

使用Vue 2

使用美元的听众:

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

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

然后我们只需要在测试组件中添加v-on="$listeners",如下所示:

测试。Vue(子组件)

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

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

演示:

Vue.component(“测试”,{ 模板: <div class="child" v-on="$listeners"> 点击这里 < / div >” }) 新Vue ({ 埃尔:“# myApp”, 数据:{}, 方法:{ testFunction:函数(事件){ console.log(测试点) } } }) Div.child{边框:5px虚线橙色;填充:20 px;} < script src = " https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js " > < /脚本> < div id =“myApp”> <测试@click = " testFunction " > < /测试> < / div >

使用$emit方法:

我们还可以使用$emit方法来实现这个目的,它可以帮助我们侦听父组件中子组件的事件。为此,我们首先需要从子组件中发出一个自定义事件,例如:

测试。Vue(子组件)

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

重要提示:始终使用串大小写作为事件名称。有关这一点的更多信息和演示,请查看这个答案:VueJS将计算值从组件传递给父组件。

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

App.vue

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

所以基本上,我们不用v-on:click或者简写@click,而是用v-on:my-event或者@my-event。

演示:

Vue.component(“测试”,{ 模板: <div class="child" @click="$emit('my-event')" > 点击这里 < / div >” }) 新Vue ({ 埃尔:“# myApp”, 数据:{}, 方法:{ testFunction:函数(事件){ console.log(测试点) } } }) Div.child{边框:5px虚线橙色;填充:20 px;} < script src = " https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js " > < /脚本> < div id =“myApp”> <测试@my-event = " testFunction " > < /测试> < / div >


使用Vue 3

使用v-bind = " $ attrs”:

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

因此,关于这个问题,我们不需要更新Vue 3中的任何内容,您的代码仍然可以正常工作,因为<div>是这里的根元素,它将自动侦听所有子事件。

示例# 1:

const {createApp} = Vue; const Test = { 模板: < div class = "孩子" > 点击这里 < / div >” }; const App = { 组件:{Test}, 设置(){ const testFunction = event => { console.log(点击“测试”); }; 返回{testFunction}; } }; createApp (App) .mount(“# myApp”); Div.child{边框:5px虚线橙色;填充:20 px;} < script src = " / / unpkg.com/vue@next " > < /脚本> < div id =“myApp”> <测试v:点击= " testFunction " > < /测试> < / div >

但是,对于包含嵌套元素的复杂组件,我们需要将属性和事件应用到<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>

从文档中可以看到:

由于JavaScript的限制,Vue无法检测到对数组的以下更改:

当你直接用索引设置一个项时,例如vm。items[indexOfItem] = newValue .使用实例 当你修改数组的长度时,例如vm.items.length = newLength

以我为例,我是在从Angular迁移到VUE时遇到这个问题的。Fix很简单,但是很难找到:

setValue(index) {
    Vue.set(this.arr, index, !this.arr[index]);
    this.$forceUpdate(); // Needed to force view rerendering
}