Como mencionó Chris Fritz (Vue.js Core Team Emeriti ) en VueCONF US 2019
si hiciéramos que Kia ingresara .native
y luego el elemento raíz de la entrada base cambiara de una entrada a una etiqueta, de repente este componente se rompe y no es obvio y, de hecho, es posible que ni siquiera lo atrape de inmediato a menos que tenga una prueba realmente buena. En cambio, al evitar el uso del .native
modificador, que actualmente considero que un antipatrón se eliminará en Vue 3 , podrá definir explícitamente que el padre podría preocuparse sobre a qué elemento se agregan los oyentes ...
Con Vue 2
Utilizando $listeners
:
Entonces, si está utilizando Vue 2, una mejor opción para resolver este problema sería usar una lógica de envoltura totalmente transparente . Para esto, Vue proporciona una $listeners
propiedad que contiene un objeto de oyentes que se utilizan en el componente. Por ejemplo:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
y luego solo necesitamos agregar v-on="$listeners"
altest
componente como:
Test.vue (componente hijo)
<template>
<div v-on="$listeners">
click here
</div>
</template>
Ahora el <test>
componente es un contenedor totalmente transparente , lo que significa que se puede usar exactamente como un <div>
elemento normal : todos los oyentes funcionarán, sin el .native
modificador.
Manifestación:
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>
Utilizando el $emit
método:
También podemos usar el $emit
método para este propósito, que nos ayuda a escuchar eventos de componentes secundarios en el componente principal. Para esto, primero debemos emitir un evento personalizado desde un componente secundario como:
Test.vue (componente hijo)
<test @click="$emit('my-event')"></test>
Importante: siempre use kebab-case para los nombres de los eventos. Para obtener más información y una demostración de este punto, consulte esta respuesta: VueJS pasa el valor calculado del componente al padre .
Ahora, solo necesitamos escuchar este evento personalizado emitido en el componente principal como:
App.vue
<test @my-event="testFunction"></test>
Entonces, básicamente en lugar de v-on:click
o la taquigrafía @click
simplemente usaremos v-on:my-event
o simplemente @my-event
.
Manifestación:
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>
Con Vue 3
Utilizando v-bind="$attrs"
:
Vue 3 nos hará la vida mucho más fácil de muchas maneras. Uno de los ejemplos es que nos ayudará a crear un contenedor transparente más simple con muy poca configuración simplemente usandov-bind="$attrs"
. Al usar esto en componentes secundarios, no solo nuestro oyente trabajará directamente desde el padre, sino que también cualquier otro atributo también funcionará de manera normal <div>
.
Entonces, con respecto a esta pregunta, no necesitaremos actualizar nada en Vue 3 y su código seguirá funcionando bien, ya que <div>
es el elemento raíz aquí y escuchará automáticamente todos los eventos secundarios.
Demo # 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>
Pero para componentes complejos con elementos anidados donde necesitamos aplicar atributos y eventos a main en <input />
lugar de a la etiqueta principal, simplemente podemos usarv-bind="$attrs"
Demo # 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.native="testFunction"