3. VUE. Navegación e Comunicación entre Compoñentes

De MediaWiki
Ir a la navegación Ir a la búsqueda

Obxectivos da unidade (6 horas)

Exploraremos a navegación dentro dunha SPA usando Vue Router, aprenderemos a definir rutas, usar RouterLink e RouterView, traballar con parámetros dinámicos, e comunicaremos compoñentes mediante props e eventos personalizados, aplicando estilos reactivos con v-bind.

  • Integración de elementos fixos como navbar e footer en múltiples vistas
  • Comprender e aplicar o enrutamento en Vue para crear múltiples vistas dentro dunha SPA, rutas básicas, navegación entre compoñentes.
  • Aprender a comunicar compoñentes entre si mediante props e eventos personalizados, envío de eventos mediante $emit.
  • Aplicar novos estilos dinámicos usando v-bind:class e v-bind:style

Compoñentes fixos

Antes de mergullarnos de cheo no enrutamento, é interesante comezar definindo dous compoñentes que van aparecer na maioría das páxinas qué o encabezamento ou Header e o pé ou Footer. Para nós serán os compoñentes NavBar.vue e o FooTer.vue situado no almacén desta unidade.

Estes elementos adoitan considerarse compoñentes fixos en moitas webs, en maioría delas non cambian ao navegar entre rutas o páxinas da web. Grazas a eles podemos crear unha estrutura común en toda a aplicación: un menú na parte superior que permita moverse entre vistas, e un pé de páxina que complete o deseño.

Imos descargar dende o almacén deste módulo os compoñentes e NavBar.vue e FooTer.vue.

A continuación engadímolos na carpeta components e facemos as modificacións que se sinalan en App.vue para que se poidan cargar.

O resultado final sería xa se aprecia o NavBar no navegador da aplicación e o típico FooTer sinxelo.

O código máis sinxelo é o de FooTer.vue, onde pode aparecer aspectos como avisos legais, contactos, acerca de....

O código de NavBar.vue é onde básicamente contén o menú, logo, búsqueda e si fora o caso, o acceso de usuarios sería este e o icono hamburguesa para dispositivos móviles.

No noso caso:

Dentro do código aparécenos un componente especial router-link da librería de Vue Router. router-link permítenos navegar entre os diferentes compoñentes. Básicamente, renderiza internamente unha etiqueta <a> e permite navegar sen recargar a páxina.

Imos a configura o sistema de enrutamento de Vue.

Configuración vue-router

Vue Router é a librería oficial de enrutamento para aplicacións creadas con Vue.js. Permite converter unha aplicación común nunha SPA (Single Page Application), onde a navegación entre “páxinas” non require recargar o navegador.

Con Vue Router podemos:

  • Crear varias vistas dentro dunha mesma aplicación.
  • Definir rutas como nunha web tradicional: /inicio, /clientes, /acercadenos...).
  • Navegar entre elas sen perda de estado.
  • Traballar con parámetros, rutas anidadas, protección de rutas, etc.

Nunha SPA real, ao cambiar de páxina non se carga outra vez o HTML, só se troca o compoñente que se está mostrando. Vue Router encárgase de xestionar este proceso.

Elementos básicos

Precisa para elo dos seguintes elementos básicos:

RouterView
é o compoñente que dí a Vue onde debe renderizar a vista actual.
RouterLink
é o compoñente equivalente aos enlaces <a> nunha web tradicional, pero non é unha etiqueta HTML, senón un compoñente de Vue que evita a recarga da páxina
Historial (createWebHistory)
Vue Router xestiona a navegación usando un modo de historial:
createWeHistory()
usa a API moderna do navegador (pushState), sen "#".
createWebHashHistory()
usa "#", máis compatible pero menos "elegante".
Parámetros
permiten rutas dinámicas (/usuario/123...)
Navegación programática
as veces necesitamos abrir ligazóns que non están no menú principal ou ben lanzalo dende un método de JS.
import { useRouter } from 'vue-router'

const router = useRouter()

router.push('/inicio')

O seguinte esquema conceptual preséntanos de forma más resumida o anterior.

Concepto Que é Para que serve
Ruta Obxecto configuración Indica que compoñente se mostra nunha URL
RouterLink Compoñente de navegación Cambia de páxina sen recargar
RouterView Zona dinámica Visualiza o compoñente asociado á ruta actual
Parámetros Datos na URL Permiten rutas dinámicas (ex: /usuario/12)
useRoute Hook Ler información da ruta (params, query...)
useRouter Hook Navegar por código (push, replace...)
Historial Modo de navegación Controla como se ve a URL

A utilización de Vue Router, ademais de darlle un aspecto máis profesional, permite unha organización máis limpa do proxecto, traballar con CRUDs complexos, autenticación e outras prácticas dende o inicio.

Parte práctica

comecemos pola parte práctica.

Paso 1. Instalación de Vue-Router

npm install vue-router

Paso 2. Crear a carpeta router e dentro dela o arquivo index.js

Pódese chamar de outra maneira pero ese nome é o habitual.

Paso 3. Contido de index.js

Dentro de ese arquivo imos a establecer o contido básico seguinte de acordo o noso menú.

import { createRouter, createWebHistory } from 'vue-router'
import Inicio from './components/Inicio.vue'
import XestionUsuarios from './components/XestionUsuarios.vue'
import XestionTarefas from './components/XestionTarefas.vue'
import SobreNos from './components/SobreNos.vue'
import AvisoLegal from './components/AvisoLegal.vue'

const routes = [
  { path: '/', name: 'inicio', component: Inicio },
  { path: '/usuarios', name: 'usuarios', component: XestionUsuarios },
  { path: '/tarefas', name: 'tarefas', component: XestionTarefas },
  { path: '/sobrenos', name: 'sobrenos', component: SobreNos },
  { path: '/avisolegal', name: 'avisolegal', component: AvisoLegal}
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

Paso 4. Indicar en main.js uso de router

Imos a main.js para dicirlle a aplicación que ten que usar router.

Antes de seguir avanzando, nestes momentos vemos que o entorno de programación pode comezar a darnos un erro, en realidade varios, e tantos como compoñentes que declaramos, e que aínda non codificamos xa que, de momento, só temos XestionUsuarios.vue.

Por iso, imos crear uns compoñentes básicos, de momento sen ningunha funcionalidade e así corrixir os erros e, ademais, permitiranos comprender como funciona vue-router.

Neste punto non nos preocupa o contido das páxinas, senón comprender como funciona a navegación.

É importante que os nomes coincidan en maiúsculas e minúsculas, especialmente en Linux.

Os códigos, de momento, podría ser algo así:

IniCio.vue XestorTarefas.vue SobreNos.vue AvisoLegal.vue
<template>
     <h2>Inicio</h2>
</template>
<script setup></script>
<style scoped></style>
<template>
     <h2>Tarefas</h2>
</template>
<script setup></script>
<style scoped></style>
<template>
       <h2>Sobre Nos</h2>
</template>
<script setup></script>
<style scoped></style>
<template>
       <h2>Aviso Legal</h2>
</template>
<script setup></script>
<style scoped></style>

Agora paramos o servidor (Ctrl+C) e volvemos a arrincalo e veremos que deixa de dar erros.

NOTA.
En realidade, tampouco é estritamente necesario paralo xa que a medida que facemos modificacións no entorno está continuamente probando se todo funciona. Sen embargo, as veces convén facelo.

A imaxe mostra como debería ser a disposición final.

Volvemos arrincar:

Paso 5. Modificar App.vue para que non chame directamente a XestiónUsuarios.vue

Quedamos modificar o App.vue. Ata agora, chamaba directamente a XestiónUsuarios.vue, pero non é que queremos, o que buscamos e que cargue a páxina inicial “/” e que poña “a funcionar” o enrutamento, cargando a función router-view. Para mellorar o deseño fai os cambios que se indican na zona style o ben podes tomar o arquivo App.vue do almacén da Unidade.

Se nos fixamos no arquivo index.js, o que arrincará por defecto é o compoñente Inicio.vue ("/")

Unha vez feito, xa podemos navegar polos diferentes ligazóns da aplicación.

Nas seguinte imaxes móstranos como funciona Vue Router.

Basicamente, cada vez que pulsamos un ligazón (router-link), a librería Vue Router busca en index.js a que compoñente da ruta se corresponde para que Vue poda chamalo e cargalo.

As páxinas vanse cargando segundo as vamos chamando.

En resumo, podemos pensar en <router-view> como unha “ventá” que cambia de contido segundo a ruta activa.

Comunicación entre compoñentes

Unha vez que o número e compoñentes crece a nosa aplicación poderá, e probablemente terá, que "falar" entre eles.

A comunicación entre compoñentes en Vue refírese ao intercambio de datos e eventos entre distintos compoñentes dunha aplicación, permitindo que un compoñente “pai” pase información aos seus “fillos” mediante props, e que estes envíen de volta eventos usando $emit.

  • Compoñente Pai: é o que ten o control dos datos principais. Exemplo: XestionUsuarios.vue que contén a lista de usuarios.
  • Compoñente Fillo: recibe datos do pai e pode facer operacións sobre eses datos ou amosalos. Exemplo: XestionTarefas.vue que amosa as tarefas dun usuario concreto.

O fillo non modifica directamente os datos do pai, só recibe información ou envía eventos para que o pai actúe.

As ferramentas que usamos en Vue para a comunicación son:

Concepto – Directiva Función
props Permite ao pai pasar datos ao fillo.
Exemplo: <Fillo :usuario="usuarioSeleccionado"/>
$emit Permite ao fillo enviar eventos ao pai.
Exemplo: emit('tarefaActualizada', novaTarefa)
v-model Pode usarse en compoñentes fillos para crear unha comunicación bidireccional simplificada (vincula prop + evento)
computed Útil no pai para calcular datos derivados.
Exemplo: contar tarefas dun usuario
v-for No fillo, para renderizar listas de datos que recibe do pai
v-if Mostrar elementos no fillo só se hai datos dispoñibles


Parte práctica

No almacén da Unidade 3 temos o compoñente XestiónTarefas.vue co código que nos permite mostrar as tarefas dun usuario. Simplemente, de momento, é unha táboa para mostrar as tarefas e as referencias no script as mesmas. Interesante as directivas:

  • v-if: se non hai usuario seleccionado manda un mensaxe de que se elixa un usuario
  • v-else-if: se hai usuario seleccionado pero non ten tarefas pois manda un mensaxe de que non ten tarefas

Sempre que produza un cambio consultará estas directivas.

De momento, son todas iguais. Cando empecemos na UD4 entón poderemos acercarnos a un suposto máis real. Incluímolo na carpeta components

Comunicación pai-fillo

Empezamos modificando o compoñente pai ou XestiónUsuarios.vue.

A idea é que cando premamos e cargemos os datos dun usuario no formulario, o compoñente fillo, XestionTarefas.vue, cargue tamén as súas tarefas.

Zona script. Engadimos dúas cousas:

  • unha referencia para o usuario seleccionado e as súas tarefas no pai
  • aproveitando o botón do lapis ou editar cargar os datos de usuario en usuario seleccionado e as tarefas

Zona Template: engadimos a referencia a XestionTarefas.vue. Esto oblíganos a que na zona de script, teñamos que importar o compoñente fillo.

Estas últimas liñas son as que conectan o compoñente pai (XestionUsuarios.vue) co fillo (XestionTarefas.vue) mediante props, que son como “canles de comunicación de datos unidireccionais”.

  • :usuario="usuarioSeleccionado": está a pasar un obxecto usuarioSeleccionado do pai ao fillo. Coma xa dixemo o termo “:” é un shorthand (expresión curta) de v-bind, que indica que o valor é unha expresión de JavaScript (non un string literal). No fillo, podemos acceder a ese obxecto directamente co nome usuario.
  • :tarefas="tarefasDoUsuario": de igual forma, pasa ao fillo a lista de tarefas correspondente ao usuario seleccionado. No fillo, está dispoñible como tarefas.

É importante porque:

  • Permite que o fillo se manteña reactivo: cada vez que o pai cambia usuarioSeleccionado ou tarefasDoUsuario, o fillo actualízase automaticamente sen que o compoñente teña que facer nada.
  • Mantén unha separación de responsabilidades: o pai controla os datos e o fillo só os amosa.
  • Facilita o fluxo de datos de comunicación unidireccional, que é a forma recomendada en Vue para evitar problemas de sincronización ou efectos colaterais.

Aínda que non é unha limitación técnica, senón unha consecuencia do fluxo de datos entre compoñentes pai → fillo, o uso de props implica que temos que renderizar no mesmo compoñente, neste caso no compoñente pai, o compoñente fillo, neste caso as tarefas o que as veces non é algo visualmente atractivo, como se pode ver na imaxe.

Comunicación fillo-pai

Ata agora vimos comunicación de pai a fillo. Agora imos ver que pasa cando o fillo necesita avisar ao pai de que ocorreu algo. Para iso usase $emit.

O compoñente XestiónUsuarios.vue, que é o compoñente pai, tamén podera recibir sinais do fillo mediante o sinal $emit.

Faremos algo sinxelo e realista:

  • Marcar unha tarefa como completada desde XestionTarefas.vue
  • O fillo emite: “esta tarefa cambiou”
  • O pai decide que facer con ese cambio

Para iso en XestiónTarefas.vue só engadimos o seguinte que permite que o componente fillo envíe unha sinal ao componente pai o XestiónUsuarios.vue.

Mentres, no compoñente XestionUsuarios.vue engadimos a función marcar a tarefa completada.

Resumo

  • XestionUsuarios.vue é o pai, porque ten os datos reais.
  • XestionTarefas.vue é o fillo, porque só amosa e interactúa.
  • O fillo non cambia datos directamente.
  • Cando pasa algo importante, usa $emit, é o pai decide como actualizar o estado da tarefa.

Nas imaxes posteriores vemos o funcionamento.

Se pulsamos en Accións na Tarefa 1, un evento dende o compoñente fillo rexistrase no compoñente pai.

Resumo

  • Enrutamento en Vue: Comprender e aplicar rutas para navegar entre compoñentes dentro dunha SPA.
  • Rutas comodín: Xestionar URLs inexistentes mostrando un compoñente de “Páxina non encontrada”
  • Comunicación pai → fillo: Pasar datos mediante props para amosar información adicional nos compoñentes fillos.
  • Estado básico compartido: Mantemento de datos seleccionados e actualización de compoñentes relacionados sen backend

Tarefa

Trátase de entender como funciona o enrutamento, engadindo novos compoñentes e a diferencia entre pai e fillo e o uso de props para pasar datos e $emits para comunicar eventos.



Exercicio 1. Rutas comodín (pathMatch)

Cando unha ligazón non existe, está mal escrita ou non coincide con ningunha das rutas definidas, entra en funcionamento a ruta comodín pathMatch. Na maioría das webs isto tradúcese nunha páxina tipo “Páxina non atopada (404)”.

En Vue, este comportamento xestiónase no ficheiro do router index.js mediante a seguinte ruta:

{
  path: '/:pathMatch(.*)*',
  name: 'NotFound',
  component: NotFound
}

Esta ruta actúa como último recurso: só se executa cando ningunha das rutas anteriores coincide coa URL.

A tarefa consiste en:

Paso A. Crear un compoñente NotFound.vue dentro do directorio components.
O compoñente debe mostrar:
  • unha mensaxe indicando que a páxina solicitada non existe: "A páxina que buscas non se atopa"
  • opcionalmente, unha imaxe para facelo máis visual. Existe unha no almacén da unidade que podes usar 404.png. As imaxes deben colocarse no directorio assets.
Paso B. Configurar a ruta comodín
Importar o compoñente NotFound.vue no ficheiro index.js do router.
import NotFound from '@/components/NotFound.vue'
Engadir a ruta comodín ao final da lista de rutas, para non interferir co resto.
{
  path: '/:pathMatch(.*)*',
  name: 'NotFound',
  component: NotFound
}
Paso C. Escribe na URL unha ligazón que non exista por exemplo
http://localhost:5173/clientes
e debería mostrar o compoñente NotFound.vue


Exercicio 2. Comunicación pai → fillo mediante props

Actualmente temos dous compoñentes:

  • Pai: XestiónUsuarios.vue que contén a lista de usuarios e as súas tarefas.
  • Fillo: XestionTarefas.vue que mostra as tarefas do usuario seleccionado.

A tarefa consiste en engadir un prop adicional ao fillo para que mostre unha mensaxe extra, sen modificar as tarefas nin usuarios existentes indicando que hai tarefas pendentes.

Paso 1. Crear un novo prop no fillo:

  • Nome do prop: mensaxeExtra
  • Tipo: String
  • Valor por defecto: " "

Paso 2. Amosar a mensaxe no fillo:

  • Se mensaxeExtra contén texto, mostrárao nun <p> por exemplo, despois da táboa de tarefas.
  • No pai, ao incluír o fillo, pasar o prop con calquera mensaxe que queiras que se vexa, por exemplo: "Lembra revisar tarefas pendentes"

Paso 3. Comprobar funcionamento:

  • Ao seleccionar un usuario, ademais de ver as súas tarefas, tamén debería aparecer a mensaxe extra se ten tarefas pendentes.
  • Se non ten tarefas pendentes non pasa ningunha mensaxe, non debe mostrarse nada adicional.
  • Se cambias unha tarefa pendente a completa verifica con computed se queda algunha pendente.

Pistas útiles:

  • Recorda usar defineProps() no fillo para declarar o prop.
  • No template do fillo, podes usar v-if para mostrar a mensaxe só cando existe:
<p v-if="mensaxeExtra && tarefasPendentes">{{ mensaxeExtra }}</p>
  • No pai, os props pásanse cun:
<XestionTarefas :usuario="usuarioSeleccionado" :tarefas="tarefasUsuario" mensaxeExtra="Revisa pendentes"/>
  • No código de XestionTarefas.vue unha directiva computed que estea vixiando se hai cambios:
const tarefasPendentes = computed(() => {
    return props.tarefas.filter ......     # recorda que tarefas está dentro de props    
})


Exercicio 3. Comunicación fillo → pai mediante $emit

Por último, imos engadir un $emit, recorda de fillo a pai, moi sinxelo.

En XestionUsuarios.vue engadimos unha variable aviso:

const aviso = ref("")

Aproveitando o $emit de tarefa-completada en XestionTarefas.vue modificamos o evento marcarTarefaCompletada de XestionUsuarios.vue para que ademais de marcar a tarefa, lance unha mensaxe:

aviso.value = `Tarefa "${tarefasUsuario.value[index].titulo}" completada !!!`;

Só faltaría engadir no template do compoñente pai un atributo que recolla o aviso:

<p v-if="aviso">{{ aviso }}</p>