Vue v-on:单击对组件无效

我试图在组件内使用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>
238162 次浏览

如果你想监听组件根元素上的本机事件,你必须对v-on使用.native修饰符,如下所示:

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

组件的本机事件不能直接从父元素访问。相反,你应该尝试v-on:click.native="testFunction",或者你也可以从Test组件发出事件。像v-on:click="$emit('click')"

有点啰嗦,但我是这样做的:

< p > @click="$emit('click', $event)"
< br >

由@sparkyspider添加的示例

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

div-container组件中…

<template>
<div @click="$emit('click', $event);">The inner div</div>
</template>

它是@Neps的回答,但有细节。


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


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

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

如何处理点击事件

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

主文件

<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允许将特定值与事件一起传递。在本机click的情况下,值为MouseEvent(与Vue无关的JS事件)。

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

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

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

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

使用Vue 2

使用$listeners:

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

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

然后我们只需要将v-on="$listeners"添加到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方法来实现这个目的,它可以帮助我们在父组件中监听子组件的事件。为此,我们首先需要从子组件中发出自定义事件,例如:

测试。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('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>

但是,对于包含嵌套元素的复杂组件,我们需要将属性和事件应用到<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无法检测到对数组的以下更改:

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

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

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

使用@click.native的一个用例是当你创建一个自定义组件,你想监听自定义组件上的点击事件。例如:

#CustomComponent.vue
<div>
<span>This is a custom component</span>
</div>


#App.vue
<custom-component @click.native="onClick"></custom-component>

@click.native总是适用于这种情况。

App.vue

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

Test.vue

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