Ciclo de vida (Lifecycle hooks)

Cuando trabajamos con componentes (o instancias) de Vue, dichos componentes van pasando por una serie de fases que conforman lo que se define como el ciclo de vida de un componente de Vue. En un principio, puede parecer complejo, pero el ciclo de vida es bastante sencillo si entendemos unos conceptos básicos específicos.

Fases del ciclo de vida

Observa el siguiente diagrama donde se muestra el ciclo de vida de Vue. La primera de las fases sería beforeCreate, que es cuando se inicializa y comienza a crear el componente. Ten en cuenta que en el diagrama, las fases en color verde claro son las que vamos a tomar como referencia, mientras que las fases en fondo oscuro (precedidas por before) ocurren justo antes de su pareja en verde:

Vue: Ciclo de vida

El circulo rojo muestra el momento en el que un componente se ha cargado y mostrado en la página, se dice que el componente está montado. A lo largo de estas fases, van ocurriendo ciertos detalles, por lo que es necesario comprender bien cada una de las fases para saber en que momento es necesario hacer cada cosa.

Cada componente pasaría, como hemos dicho, por cada una de las siguientes fases:

Hook ¿Cuándo se llama? $el $data
beforeCreate Justo después de ser inicializado y antes de procesar opciones. null {}
created Después de crearse. Los datos ya están disponibles. null ✔️
beforeMount Inmediatamente antes de la fase de montaje en el DOM. null ✔️
mounted Al mostrarse en la página. No se garantiza montaje de hijos. ✔️ ✔️
beforeUpdate Cuando cambian los datos (antes de modificar el DOM). ✔️ ✔️
updated Cuando cambian los datos y el DOM ha sido modificado. ✔️ ✔️
beforeUnmount Justo antes del desmontaje, pero siendo aún funcional. ✔️ ✔️
unmounted Justo cuando un componente (y todos sus hijos) ha sido desmontado. ✔️ ✔️
activated Cuando un componente dinámico con <keep-alive> es activado. ✔️ ✔️
deactivated Cuando un componente dinámico con <keep-alive> es desactivado. ✔️ ✔️

OJO: En Vue 2, la fase beforeUnmount se llama beforeDestroy, mientras que la fase unmounted se llama destroyed. En Vue 3 se modifican los nombres para conseguir algo más acorde.

En código, cada una de estas fases se manifiesta como una especial llamada hook de ciclo de vida. Esta función es autoejecutada por Vue cuando el componente se encuentra en la correspondiente fase, ejecutando todo el código que tengamos en su interior.

Por lo tanto, es necesario definir los métodos que necesitemos en el objeto de opciones de Vue, junto a data, methods u otras opciones. Veamos un ejemplo de como hacerlo en código, utilizando el hook de la fase mounted:

<script>
export default {
  data() {
    return {
      author: "Manz"
    }
  },
  mounted() {
    console.log("El componente ha sido montado.");
  }
}
</script>

El hook mounted() será ejecutado justo cuando el componente haya terminado la fase de montaje, por lo que es útil para escribir lógica Javascript que queremos que se ejecute cuando se inicia el componente y se monta y representa visualmente en la página. De la misma forma podemos utilizar otros hooks como created(), updated() o beforeUnmount(), por ejemplo.

Ten en cuenta que propiedades especiales como this.$el o this.$data no estarán disponibles hasta a partir de la fase mounted() y created() respectivamente.

Propiedades de instancia

En el interior de estos hooks que comentabamos anteriormente, podemos utilizar ciertas propiedades especiales internas de Vue (podemos identificarlas porque comienzan por $) que hacen referencia a detalles específicos de un componente Vue.

Son las siguientes propiedades internas:

Propiedades Descripción
.$el Referencia al elemento raíz del <template> del componente/instancia.
.$props Referencia a un objeto con los props del componente/instancia.
.$data Referencia a un objeto con los datos de data en el componente/instancia.
.$options Referencia al objeto de opciones de la Option API de Vue.
.$slots Colección de referencias a slots. Ej: this.$slot.name() referencia a v-slot:name.
.$refs Colección de registrados con el atributo ref.
.$attrs Colección de atributos y eventos no reconocidos como props o custom events.

Por ejemplo, si desde un código Javascript queremos hacer referencia al elemento HTML raíz del <template>, deberemos hacer referencia a this.$el. Ten en cuenta que dicha referencia sólo estará disponible a partir de mounted, si intentamos acceder a ella en una fase previa obtendremos null:

<template>
  <div>Ejemplo de ciclo de vida</div>
</template>

<script>
export default {
  data() {
    return {
      author: "Manz"
    }
  },
  mounted() {
    console.log("El elemento raíz del template es ", this.$el);
    console.log("Los datos son accesibles en: ", this.$data.author);
    console.log("O también (a través de un proxy) en: ", this.author);
  }
}
</script>

De la misma forma, podemos acceder a this.$props para acceder a los props del componente, a this.$options para acceder al objeto de opciones de Vue, así como acceder a this.$slots o this.$refs para obtener una colección de slots o de referencias a elementos HTML (lo veremos más adelante).

Por su parte, la propiedad this.$attrs incorpora atributos indicados en el componente que no son props, así como eventos que no sean custom events.

Propiedades estructurales

Existen ciertas propiedades específicas para acceder a la estructura de componentes. No son ideales para utilizar a largo plazo o gran escala, pero en proyectos muy pequeños o casos muy concretos, puede ser una buena solución para no aumentar la complejidad del componente:

Propiedades Descripción
.$parent Referencia al componente padre del componente actual.
.$root Referencia al componente raíz (referencia a la instancia de main.js).
.$children Referencia a un array de componentes hijos del componente actual (sólo disponible en Vue 2).

Por ejemplo, imaginemos el siguiente ejemplo donde tenemos un componente padre llamado App.vue el cuál va a contener un componente hijo BaseBlock:

<template>
  <div>
    <BaseBlock></BaseBlock>
  </div>
</template>

<script>
import BaseBlock from "./components/BaseBlock.vue";

export default {
  name: "App",
  components: {
    BaseBlock
  },
  data() {
    return {
      title: "Soy el componente App.vue"
    }
  }
}
</script>

Luego, dentro del componente BaseBlock.vue tenemos una variable title en la zona data, la cuál obtiene su valor de la variable title de su componente padre (es decir, el componente al que hacemos referencia con this.$parent):

<template>
  <div>
    {{ title }}
  </div>
</template>

<script>
export default {
  name: "BaseBlock",
  data() {
    return {
      title: this.$parent.title;
    }
  }
}
</script>

Ten en cuenta que en la mayoría de los casos, esta forma de acceder a información crea un acoplamiento muy grande entre componentes, y lo ideal sería optar por utilizar un Almacén de estados como VueX o un patrón de Bus de Eventos que pueden permitir gestionar mejor la información de forma global, y sobre todo, de forma más organizada, sin necesidad de acoplar componentes.

Manz
Publicado por Manz

Docente, divulgador informático y freelance. Autor de Emezeta.com, es profesor en la Universidad de La Laguna y dirige el curso de Programación web FullStack y Diseño web FrontEnd de EOI en Tenerife (Canarias). En sus ratos libres, busca GIF de gatos en Internet.