Curso de VUE 3 – Sistema de componentes

Curso de Vue 3 – Sistema de Componentes

Los componentes son una de las características más importantes de Vue, ya que permiten la creación de aplicaciones web modulares, reutilizables y escalables.

En Vue 3, existen varias formas de crear componentes, siendo los Componentes de Archivo Único (SFC, por sus siglas en inglés) la opción más popular y recomendada debido a su simplicidad y organización.

¿Qué es un Componente de Archivo Único (SFC)?

Los Componentes de Archivo Único (.vue) son la forma más común y recomendada de crear componentes en Vue.

En un solo archivo, puedes definir el template (HTML), el script (JavaScript, TypeScript, etc.) y los estilos (CSS, SCSS, etc.), proporcionando una estructura clara y fácil de manejar.

Aquí tienes un ejemplo básico de un SFC:

<template>
  <div class="container">
    <h1 v-text="message" />
  </div>
</template>
<script setup>
  import { ref } from 'vue';
  const message = ref('Hello, Vue 3!');
</script>
<style scoped>
.container {
  color: blue;
}
</style>

Componentes Funcionales

Los componentes funcionales son componentes sin estado y sin instancia de Vue, lo que significa que son más ligeros y eficientes cuando no se necesita mantener datos reactivos o manejar el ciclo de vida del componente.

Creación de Componentes con Vue 3

En Vue 3, los componentes se pueden crear principalmente de dos formas: utilizando la Options API o la Composition API con <script setup>. Nos centraremos en la creación de componentes utilizando Componentes de Archivo Único (SFC) y ambas APIs.

Ejemplo con la Options API

Aquí tienes un ejemplo básico de un componente creado con la Options API:

<template>
  <div>
    {{ message }}
  </div>
</template>
<script>
export default {
  data() {
    return {
      message: 'Hello, World!'
    }
  }
}
</script>
<style scoped>
  div {
    font-size: 24px;
    color: red;
  }
</style>

En este ejemplo, hemos definido un componente llamado HelloWorld que muestra un mensaje en la pantalla. Utilizamos la opción data para definir el estado del componente y mostramos el mensaje usando la sintaxis de plantillas de Vue ({{ message }}).

Ejemplo con la Composition API y <script setup>

Ahora, veremos el mismo componente utilizando la Composition API y <script setup>:

<template>
  <div>
    {{ message }}
  </div>
</template>
<script setup>
  import { ref } from 'vue';
  const message = ref('Hello, World!');
</script>
<style scoped>
  div {
    font-size: 24px;
    color: red;
  }
</style>

En este ejemplo, utilizamos la función ref de la Composition API para crear una variable reactiva message y mostramos el mensaje en la pantalla.

Comunicación entre Componentes: Props y Eventos

Vue permite la comunicación entre componentes mediante el paso de props y la emisión de eventos.

Ejemplo de Paso de Props y Eventos con Options API

A continuación, creamos un componente Button que acepta una prop label y emite un evento click:

<template>
  <button @click="emitClick">
    {{ label }}
  </button>
</template>
<script>
export default {
  props: {
    label: {
      type: String,
      required: true
    }
  },
  methods: {
    emitClick() {
      this.$emit('click');
    }
  }
}
</script>

El componente Button acepta una prop label para el texto del botón y emite un evento click cuando se hace clic en él. Luego, lo utilizamos en un componente padre:

<template>
  <div>
    <button-component label="Click me!" @click="handleClick"></button-component>
  </div>
</template>
<script>
  import ButtonComponent from './Button.vue';
  export default {
    components: {
      ButtonComponent
    },
    methods: {
      handleClick() {
        alert('Button was clicked!');
      }
    }
  }
</script>

Ejemplo con Composition API y <script setup>

Aquí tienes el mismo componente Button utilizando la Composition API:

<template>
  <button @click="emitClick">
    {{ label }}
  </button>
</template>
<script setup>
  import { defineProps, defineEmits } from 'vue';
  const props = defineProps({
    label: {
      type: String,
      required: true
    }
  });
  const emit = defineEmits(['click']);
  function emitClick() {
    emit('click');
  }
</script>

Y su uso en un componente padre:

<template>
  <div>
    <button-composition label="Click me!" @click="handleClick"></button-composition>
  </div>
</template>
<script setup>
  import ButtonComposition from './ButtonComposition.vue';
  function handleClick() {
    alert('Button was clicked!');
  }
</script>

Uso de Slots para Contenido Distribuido

Los slots permiten la creación de componentes con contenido distribuido, inyectando contenido desde el componente padre al hijo.

Ejemplo de Slots con Options API

Un componente Card con slots podría verse así:

<template>
  <div class="card">
    <div class="card-header">
      <slot name="header"></slot>
    </div>
    <div class="card-body">
      <slot></slot>
    </div>
    <div class="card-footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>
<script>
  export default {}
</script>
<style scoped>
  .card {
    border: 1px solid #ccc;
    margin-bottom: 1rem;
  }
  .card-header {
    background-color: #f5f5f5;
    border-bottom: 1px solid #ccc;
    padding: 0.5rem 1rem;
  }
  .card-body {
    padding: 0.5rem 1rem;
  }
  .card-footer {
    background-color: #f5f5f5;
    border-top: 1px solid #ccc;
    padding: 0.5rem 1rem;
  }
</style>

Y su uso:

<template>
  <div>
    <card-component>
      <template #header>
        <h3>Card Header</h3>
      </template>
      <p>Card Body</p>
      <template #footer>
        <p>Card Footer</p>
      </template>
    </card-component>
  </div>
</template>
<script>
  import CardComponent from './Card.vue';
  export default {
    components: {
      CardComponent
    }
  }
</script>

Componentes Dinámicos y Asincrónicos

Los componentes dinámicos se renderizan en función de condiciones y los componentes asincrónicos se cargan de manera dinámica. Se puede utilizar la directiva v-if para manejar los componentes dinámicos y defineAsyncComponent para los asincrónicos.

Ejemplo con Composition API y <script setup>

Aquí tienes un ejemplo de componente asincrónico:

<template>
  <div>
    <button @click="changeComponent">Change Component</button>
    <component :is="currentComponent"></component>
  </div>
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue'

const ComponentA = defineAsyncComponent(() => import('./ComponentA.vue'))
const ComponentB = defineAsyncComponent(() => import('./ComponentB.vue'))
const ComponentC = defineAsyncComponent(() => import('./ComponentC.vue'))

const currentComponent = ref(ComponentA)

function changeComponent() {
  const components = [ComponentA, ComponentB, ComponentC]
  const index = components.indexOf(currentComponent.value)
  currentComponent.value = components[(index + 1) % components.length]
}
</script>

Hooks del Ciclo de Vida de los Componentes

Vue proporciona hooks para manejar distintas fases del ciclo de vida de un componente, como beforeCreate, created, mounted, y más.

Ejemplo con Options API

Aquí tienes un ejemplo de cómo usar los hooks del ciclo de vida con la Options API:

<template>
  <div>
    {{ message }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: ''
    }
  },
  beforeCreate() {
    this.message = 'beforeCreate'
    console.log('beforeCreate')
  },
  created() {
    this.message = 'created'
    console.log('created')
  },
  beforeMount() {
    this.message = 'beforeMount'
    console.log('beforeMount')
  },
  mounted() {
    this.message = 'mounted'
    console.log('mounted')
  },
  beforeUpdate() {
    this.message = 'beforeUpdate'
    console.log('beforeUpdate')
  },
  updated() {
    this.message = 'updated'
    console.log('updated')
  },
  beforeUnmount() {
    this.message = 'beforeUnmount'
    console.log('beforeUnmount')
  },
  unmounted() {
    this.message = 'unmounted'
    console.log('unmounted')
  }
}
</script>

Ejemplo con Composition API y <script setup>

Aquí tienes un ejemplo de uso de hooks del ciclo de vida:

<template>
  <div>
    {{ message }}
  </div>
</template>

<script setup>
import { ref, onBeforeCreate, onCreated, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'

const message = ref('')

onBeforeCreate(() => {
  message.value = 'beforeCreate'
})

onCreated(() => {
  message.value = 'created'
})

onBeforeMount(() => {
  message.value = 'beforeMount'
})

onMounted(() => {
  message.value = 'mounted'
})

onBeforeUpdate(() => {
  message.value = 'beforeUpdate'
})

onUpdated(() => {
  message.value = 'updated'
})

onBeforeUnmount(() => {
  message.value = 'beforeUnmount'
})

onUnmounted(() => {
  message.value = 'unmounted'
})
</script>

Conclusión

En este módulo, hemos cubierto varios aspectos fundamentales de los componentes en Vue 3, como los Componentes de Archivo Único (SFC), la Options API, la Composition API, la comunicación entre componentes mediante props y eventos, el uso de slots, componentes dinámicos y asincrónicos, y los hooks del ciclo de vida.

Explora cada uno de estos temas en tus propios proyectos para aprovechar al máximo las capacidades de Vue 3 y construir aplicaciones web modernas y dinámicas.