10. Slots
Slot básico
Section titled “Slot básico”Los slots son un mecanismo de distribución de contenido en Vue que te permite insertar contenido en componentes. Los slots actúan como marcadores de posición en los componentes hijos donde el componente padre puede inyectar contenido.
Componente con slot básico
Section titled “Componente con slot básico”<script setup>// No es necesario definir props para recibir el contenido del slot</script>
<template> <div class="card"> <div class="card-body"> <!-- El slot recibe el contenido del componente padre --> <slot></slot> </div> </div></template>
<style scoped>.card { border: 1px solid #ddd; border-radius: 4px; padding: 15px;}</style>Uso del slot básico desde el componente padre
Section titled “Uso del slot básico desde el componente padre”<script setup>import CardComponent from './CardComponent.vue'</script>
<template> <CardComponent> <!-- Este contenido se insertará dentro del slot en CardComponent --> <h3>Título de la tarjeta</h3> <p>Este es el contenido que se mostrará dentro de la tarjeta.</p> </CardComponent></template>v-slot y sintaxis abreviada
Section titled “v-slot y sintaxis abreviada”La directiva v-slot proporciona una forma más estructurada de trabajar con slots. También tiene una sintaxis abreviada usando el símbolo #.
Uso de v-slot con template
Section titled “Uso de v-slot con template”<script setup>import ComponentWithSlot from './ComponentWithSlot.vue'</script>
<template> <ComponentWithSlot> <!-- Sintaxis completa con v-slot en template --> <template v-slot:default> <p>Este contenido va en el slot predeterminado</p> </template> </ComponentWithSlot>
<!-- El mismo ejemplo usando la sintaxis abreviada # --> <ComponentWithSlot> <template #default> <p>Usando la sintaxis abreviada para el slot predeterminado</p> </template> </ComponentWithSlot>
<!-- Para el slot default, puedes omitir el nombre cuando no hay otros slots --> <ComponentWithSlot> <template v-slot> <p>Slot predeterminado sin especificar nombre</p> </template> </ComponentWithSlot>
<!-- La forma más común (sin template cuando solo hay slot predeterminado) --> <ComponentWithSlot> <p>Contenido directo para el slot predeterminado</p> </ComponentWithSlot></template>Slots nombrados (#header, #footer)
Section titled “Slots nombrados (#header, #footer)”Los slots nombrados permiten definir múltiples puntos de distribución en un componente. Esto es especialmente útil cuando necesitas una estructura más compleja.
Definición de slots nombrados en un componente
Section titled “Definición de slots nombrados en un componente”<script setup>// No se requiere código especial para slots nombrados en Composition API</script>
<template> <div class="layout"> <header class="header"> <slot name="header"><!-- Contenido fallback si no se proporciona --></slot> </header>
<main class="content"> <slot><!-- Slot predeterminado --></slot> </main>
<footer class="footer"> <slot name="footer"><!-- Contenido fallback si no se proporciona --></slot> </footer> </div></template>Uso de slots nombrados desde el componente padre
Section titled “Uso de slots nombrados desde el componente padre”<script setup>import LayoutComponent from './LayoutComponent.vue'</script>
<template> <LayoutComponent> <!-- Contenido para el slot header usando sintaxis completa --> <template v-slot:header> <h1>Encabezado de la página</h1> </template>
<!-- El contenido sin template va al slot predeterminado --> <p>Este es el contenido principal que va en el slot predeterminado.</p>
<!-- Contenido para el slot footer usando sintaxis abreviada # --> <template #footer> <p>© 2025 - Mi Aplicación Vue</p> </template> </LayoutComponent></template>Pasar datos a los slots (scoped slots)
Section titled “Pasar datos a los slots (scoped slots)”Los slots con alcance (scoped slots) permiten que un componente hijo pase datos al contenido del slot definido por el componente padre. Esto es extremadamente útil cuando quieres que el padre personalice la visualización de los datos del hijo.
Componente hijo que pasa datos a los slots
Section titled “Componente hijo que pasa datos a los slots”<script setup>import { ref } from 'vue'
const users = ref([ { id: 1, name: 'Ana', role: 'Developer' }, { id: 2, name: 'Carlos', role: 'Designer' }, { id: 3, name: 'Elena', role: 'Manager' }])</script>
<template> <div> <h2>Lista de usuarios</h2> <ul> <li v-for="user in users" :key="user.id"> <!-- Pasamos el objeto user al slot --> <slot name="user" :user="user" :index="user.id"> <!-- Contenido predeterminado si no se proporciona slot --> {{ user.name }} </slot> </li> </ul>
<!-- Otro ejemplo con estadísticas --> <div class="stats"> <slot name="stats" :total="users.length"> Total: {{ users.length }} usuarios </slot> </div> </div></template>Componente padre que recibe y usa los datos del slot
Section titled “Componente padre que recibe y usa los datos del slot”<script setup>import UserList from './UserList.vue'</script>
<template> <div> <h1>Nuestro equipo</h1>
<UserList> <!-- Accediendo a los datos del slot usando destructuring --> <template #user="{ user }"> <div class="user-card"> <strong>{{ user.name }}</strong> <em>{{ user.role }}</em> </div> </template>
<!-- Ejemplo usando todos los props pasados --> <template #stats="slotProps"> <div class="statistics"> <p>El equipo tiene {{ slotProps.total }} miembros</p> </div> </template> </UserList> </div></template>Destructuring avanzado en scoped slots
Section titled “Destructuring avanzado en scoped slots”<template> <DataTable :items="products"> <!-- Destructuring básico --> <template #header="{ column }"> {{ column.toUpperCase() }} </template>
<!-- Renombrar propiedades durante el destructuring --> <template #item="{ item: product, index: idx }"> {{ idx + 1 }}. {{ product.name }} - ${{ product.price }} </template>
<!-- Valores por defecto durante el destructuring --> <template #footer="{ total = 0, currency = '€' }"> Total: {{ currency }}{{ total }} </template> </DataTable></template>Casos de uso comunes para slots
Section titled “Casos de uso comunes para slots”Componentes de layout
Section titled “Componentes de layout”Definir la estructura mientras permites al usuario personalizar las diferentes secciones:
<BaseLayout> <template #header> <AppHeader /> </template>
<MainContent />
<template #sidebar> <AppSidebar /> </template>
<template #footer> <AppFooter /> </template></BaseLayout>Componentes de lista con renderizado personalizado
Section titled “Componentes de lista con renderizado personalizado”<DataList :items="products"> <template #item="{ item }"> <ProductCard :title="item.name" :price="item.price" :image="item.image" /> </template>
<template #empty> <p>No hay productos disponibles</p> </template></DataList>Componentes de modal/diálogo
Section titled “Componentes de modal/diálogo”<ModalDialog title="Confirmar acción"> <p>¿Estás seguro de que deseas continuar?</p>
<template #actions> <Button @click="confirm" primary>Confirmar</Button> <Button @click="cancel">Cancelar</Button> </template></ModalDialog>