Skip to content

Eventos de los Componentes

Esta página supone que ya has leído los Fundamentos de los Componentes. Léelo primero si eres nuevo en el tema de componentes.

Emitiendo y Escuchando Eventos

Un componente puede emitir eventos personalizados directamente en las expresiones de la plantilla (por ejemplo, en un manejador v-on) utilizando el método incorporado $emit:

template
<!-- MyComponent -->
<button @click="$emit('someEvent')">Hazme clic</button>

El método $emit() también está disponible en la instancia del componente como this.$emit():

js
export default {
  methods: {
    submit() {
      this.$emit('submit')
    }
  }
}

El padre puede entonces escucharlo usando v-on:

template
<MyComponent @some-event="callback" />

El modificador .once también es compatible con los escuchadores de eventos de los componentes:

template
<MyComponent @some-event.once="callback" />

Al igual que los componentes y props, los nombres de eventos proporcionan una transformación automática de mayúsculas y minúsculas. Observa que emitimos un evento camelCase, pero podemos escucharlo usando un listener kebab-cased en el padre. Como en el caso de la Nomenclatura de las Props, recomendamos utilizar escuchas de eventos con mayúsculas en las plantillas.

TIP

A diferencia de los eventos nativos del DOM, los eventos emitidos por los componentes no se reproducen. Sólo puedes escuchar los eventos emitidos por un componente hijo directo. Si es necesario comunicarse entre componentes hermanos o profundamente anidados, utiliza un bus de eventos externo o una solución de gestión de estado global.

Argumentos del Evento

A veces es útil emitir un valor específico con un evento. Por ejemplo, podemos querer que el componente <BlogPost> se encargue de cuánto ampliar el texto. En esos casos, podemos pasar argumentos extra a $emit para proporcionar este valor:

template
<button @click="$emit('increaseBy', 1)">
  Aumentar en 1
</button>

Así, cuando escuchamos el evento en el padre, podemos utilizar una función de flecha en línea como oyente, lo que nos permite acceder al argumento del evento:

template
<MyButton @increase-by="(n) => count += n" />

O, si el manejador del evento es un método:

template
<MyButton @increase-by="increaseCount" />

Entonces el valor se pasará como primer parámetro de ese método:

js
methods: {
  increaseCount(n) {
    this.count += n
  }
}
js
function increaseCount(n) {
  count.value += n
}

TIP

Todos los argumentos adicionales que se pasen a $emit() después del nombre del evento serán reenviados a la función de escucha. Por ejemplo, con $emit('foo', 1, 2, 3) la función de escucha recibirá tres argumentos.

Declarando Eventos Emitidos

Un componente puede declarar explícitamente los eventos que emitirá utilizando la macro defineEmits()opción emits:

vue
<script setup>
defineEmits(['inFocus', 'submit'])
</script>

El método $emit que utilizamos en la <plantilla> no es accesible dentro de la sección <script setup> de un componente, pero defineEmits() devuelve una función equivalente que podemos utilizar en su lugar:

vue
<script setup>
const emit = defineEmits(['inFocus', 'submit'])

function buttonClick() {
  emit('submit')
}
</script>

La macro defineEmits() no puede ser usada dentro de una función; debe ser colocada directamente dentro de <script setup>, como en el ejemplo anterior.

Si estás usando una función explícita setup en lugar de <script setup>, los eventos deben ser declarados usando la opción emits, y la función emit es expuesta en el contexto de setup():

js
export default {
  emits: ['inFocus', 'submit'],
  setup(props, ctx) {
    ctx.emit('submit')
  }
}

Al igual que con otras propiedades del contexto setup(), emit puede ser desestructurado con seguridad:

js
export default {
  emits: ['inFocus', 'submit'],
  setup(props, { emit }) {
    emit('submit')
  }
}
js
export default {
  emits: ['inFocus', 'submit']
}

La opción emits y el macro defineEmits() también admiten una sintaxis de objeto. Si utilizas TypeScript puedes tipar argumentos, lo que nos permite realizar una validación en tiempo de ejecución del payload de los eventos emitidos:

vue
<script setup>
const emit = defineEmits({
  submit(payload: { email: string; password: string }) {
    // devuelve `true` o `false` para indicar
    // que la validación ha pasado / no ha pasado
  }
})
</script>

Si estás usando TypeScript con <script setup>, también es posible declarar eventos emitidos usando anotaciones de tipo puro:

vue
<script setup lang="ts">
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
</script>

Más detalles: Escritura de Emits del Componente

js
export default {
  emits: {
    submit(payload: { email: string; password: string }) {
      // devuelve `true` o `false` para indicar
      // que la validación ha pasado / no ha pasado
    }
  }
}

Véase también: Escritura de Emits del Componente

Aunque es opcional, se recomienda definir todos los eventos emitidos para documentar mejor cómo debe funcionar un componente. Esto también permite a Vue excluir a los escuchadores conocidos desde los atributos fallthrough, evitando situaciones críticas causadas por eventos del DOM enviados manualmente por código de terceros.

TIP

Si se define un evento nativo (por ejemplo, clic) en la opción emits, el escuchador solo escuchará ahora los eventos clic emitidos por el componente y ya no responderá a los eventos clic nativos.

Validación de Eventos

De forma similar a la validación del tipo de props, un evento emitido puede ser validado si se define con la sintaxis de objeto en lugar de la sintaxis de array.

Para agregar la validación, se asigna al evento una función que recibe los argumentos pasados a la invocación de this.$emitemit y devuelve un booleano para indicar si el evento es válido o no.

vue
<script setup>
const emit = defineEmits({
  // Sin validación
  click: null,

  // Validar el evento submit
  submit: ({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('¡Payload del evento submit inválido!')
      return false
    }
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>
js
export default {
  emits: {
    // Sin validación
    click: null,

    // Validar el evento submit
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('¡Payload del evento submit inválido!')
        return false
      }
    }
  },
  methods: {
    submitForm(email, password) {
      this.$emit('submit', { email, password })
    }
  }
}

Eventos como Props

También puedes declarar y pasar eventos como props, prefijando el nombre del evento en mayúsculas con on.

Usar props.onEvent tiene un comportamiento diferente que usar emit('event'), ya que el primero sólo manejará el listener basado en la propiedad (ya sea @event o :on-event)

WARNING

Si se pasan tanto :onEvent como @event props.onEvent podría ser un array de funciones en lugar de una sola función, este comportamiento no es estable y podría cambiar en el futuro.

Debido a esto, se recomienda usar emit('event') en lugar de props.onEvent cuando se emitan eventos.

Eventos de los Componentes has loaded