Componentes y templates
Un componente Vue es una pieza reutilizable de UI: plantilla + logica + estilos. Las plantillas declaran que se renderiza; las directivas (v-if, v-for, v-bind, v-on) conectan el DOM con el estado reactivo.
Anatomia de un SFC
vue
<script setup lang="ts">
import { ref } from 'vue'
import StatusBadge from './StatusBadge.vue'
const titulo = ref('Pedidos')
</script>
<template>
<section>
<h2>{{ titulo }}</h2>
<StatusBadge label="activo" />
</section>
</template>
<style scoped>
section {
padding: 1rem;
}
</style>scoped limita el CSS a ese componente y evita fugas de estilos.
Interpolacion y atributos
vue
<script setup lang="ts">
import { ref } from 'vue'
const producto = ref({ id: 12, nombre: 'Teclado', stock: 3 })
const imagen = '/img/teclado.png'
</script>
<template>
<article>
<h3>{{ producto.nombre }}</h3>
<img :src="imagen" :alt="producto.nombre" />
<p v-text="`Stock: ${producto.stock}`"></p>
</article>
</template>:src es atajo de v-bind:src. Usa {{ }} para texto; evita v-html con datos no confiables (XSS).
Condicionales y listas
vue
<script setup lang="ts">
import { ref } from 'vue'
const cargando = ref(false)
const items = ref([
{ id: 1, nombre: 'Vue' },
{ id: 2, nombre: 'Pinia' },
])
</script>
<template>
<p v-if="cargando">Cargando...</p>
<ul v-else>
<li v-for="item in items" :key="item.id">
{{ item.nombre }}
</li>
</ul>
<p v-show="items.length === 0">Sin resultados</p>
</template>v-ifmonta/desmonta el nodo;v-showsolo cambiadisplay.:keyestable (id) es obligatorio en listas para un diff correcto.
Eventos y modificadores
vue
<script setup lang="ts">
const onSubmit = (event: Event) => {
event.preventDefault()
console.log('enviar')
}
</script>
<template>
<form @submit.prevent="onSubmit">
<button type="submit">Guardar</button>
</form>
<input @keyup.enter="onSubmit" />
</template>Modificadores utiles: .prevent, .stop, .once, .enter.
Componentes hijos y slots
vue
<!-- CardBox.vue -->
<script setup lang="ts">
defineProps<{ titulo: string }>()
</script>
<template>
<div class="card">
<header>{{ titulo }}</header>
<slot />
<footer>
<slot name="footer" />
</footer>
</div>
</template>Uso:
vue
<CardBox titulo="Detalle">
<p>Contenido principal</p>
<template #footer>
<button type="button">Cerrar</button>
</template>
</CardBox>Los slots permiten inyectar markup sin acoplar el hijo al padre.
Jerarquia recomendada
txt
views/ -> paginas (rutas)
components/ -> piezas reutilizables (botones, cards, formularios)
composables/ -> logica compartida (cap. 4)Una view orquesta; un component presenta.
Buenas practicas
- Un solo proposito por componente; si el template supera ~150 lineas, divide.
- Prefiere props + emits a mutar estado del padre desde el hijo.
- Usa
scopedo CSS modules; evita selectores globales salvo tokens de diseno. - Nombra eventos en kebab-case en el template (
@update:model-value). - No uses
v-ifyv-foren el mismo elemento (envuelve con<template>).
Errores habituales
- Olvidar
:keyenv-foro usar el indice cuando la lista se reordena. - Confiar en
v-htmlcon HTML del usuario. - Componentes "dios" que mezclan fetch, formulario y tabla.
- Importar componentes sin registrarlos (en
script setupel import ya registra). - Estilos globales que pisan clases de librerias UI.
Ejercicios
- Crea
ProductList.vueconv-forsobre un array de productos y:keypor id. - Anade
v-ifpara estado vacio yv-showpara un aviso de stock bajo. - Extrae un
ProductItem.vuey usa un slot para acciones (editar/borrar). - Sustituye un
@click+preventDefaultmanual por@submit.prevent.
Siguiente paso
En el capitulo 3 veras ref, reactive, computed, watch y como Vue detecta cambios.
