Saltar al contenido
Framework Ecosystems LabsUn contrato, muchos ecosistemas, la misma prueba.

Clase 090 — Enrutado en el cliente#

⬅️ 089 · 📚 Parte 6 · 🎓 Clases · 091 ➡️ Parte 6 — La interfaz: del HTML del servidor al componente · Nivel 🟡 intermedio · Pista frontend (Interfaz y componentes) ✅ Clase construida — 4 implementaciones verificadas contra contrato.json.

🎯 Objetivo#

La dirección es estado. Un enrutador de cliente no es más que una tabla y una función que empareja — y como las dos cosas son lógica pura, se pueden ejecutar en el servidor.

Por eso esta clase se puede verificar entera sin navegador: pedir /tareas/42 y comprobar que sale el detalle con su parámetro es exactamente lo que hace el enrutador del cliente, solo que en otro proceso.

📚 Resultados de aprendizaje#

Al terminar podrás:

🧩 La situación#

Cuatro direcciones: la raíz, un listado, un formulario nuevo y un detalle con identificador.

Dos de ellas empiezan igual —/tareas/nueva y /tareas/:id— y ahí está la trampa: si el orden se invierte, «nueva» se convierte en un identificador y el formulario deja de existir.

Es el mismo problema de la clase 012 en el servidor, con una diferencia: aquí, en tres de los cuatro, el orden lo decides tú.

🧮 El contrato#

# Petición Qué comprueba
1 GET / la raíz lleva a inicio
2 GET /tareas/42 el detalle, con data-id="42"
3 GET /tareas/nueva la literal gana a la del parámetro
4 GET /no-existe 404, no una página en blanco
5 GET /emparejar?patron=/tareas/:id&ruta=/tareas/42 los parámetros salen de la dirección
6 GET /emparejar?patron=/tareas/:id&ruta=/tareas y no empareja de más
7 GET /rutas.json la tabla, leída del archivo

El caso 4 merece una nota, porque es el error más extendido de las aplicaciones de una sola página: el servidor devuelve el mismo HTML para cualquier dirección, el enrutador del cliente no encuentra nada, y el usuario ve una página vacía con un 200. Los buscadores la indexan como válida.

Un enrutador que se respete tiene una entrada para lo que no existe, y el servidor que lo acompaña devuelve 404.

<!-- generado: fichas -->

🧰 Las piezas de esta clase, una por una#

Antes del código: qué es cada framework, qué versión se está usando y qué hace falta para ejecutarlo. Todo lo de esta sección sale de los archivos reales del repositorio —el catálogo, la receta de arranque y el manifiesto de dependencias de cada ecosistema—, así que no puede quedarse desactualizado sin que la validación lo detecte.

Framework Qué es Desde Licencia Quién lo mantiene
React biblioteca de interfaz de JavaScript/TypeScript (JavaScript) 2013 MIT Meta y colaboradores
Vue framework de interfaz de JavaScript/TypeScript (JavaScript) 2014 MIT proyecto independiente
Angular framework de interfaz de TypeScript (TypeScript) 2016 MIT Google y colaboradores
Svelte framework de interfaz de JavaScript/TypeScript (JavaScript) 2016 MIT proyecto independiente

🔧 React#

Impuso la idea de la interfaz como función del estado y el árbol virtual. Es una biblioteca, no un framework: no arranca tu aplicación ni define su ciclo de vida.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts

Arrancarla suelta, sin el verificador:

PORT=3000 node server.mjs

Qué hay dentro de su directorio:

Archivo Qué es
ejecutar.json la receta que usa el verificador: qué hace falta, cómo se prepara y cómo arranca
enrutador.mjs código JavaScript (módulo ES)
package.json manifiesto de Node.js: nombre, tipo de módulo y dependencias con su rango de versión
pnpm-lock.yaml archivo de bloqueo: la versión exacta de cada dependencia y de sus dependencias
pnpm-workspace.yaml raíz de instalación propia, y la prohibición de ejecutar scripts al instalar
server.mjs código JavaScript (módulo ES)

🔧 Vue#

Adopción progresiva: sirve como etiqueta en una página existente o como framework completo. Su reactividad fina influyó en toda la generación siguiente.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts

Arrancarla suelta, sin el verificador:

PORT=3000 node server.mjs

Qué hay dentro de su directorio:

Archivo Qué es
ejecutar.json la receta que usa el verificador: qué hace falta, cómo se prepara y cómo arranca
enrutador.mjs código JavaScript (módulo ES)
package.json manifiesto de Node.js: nombre, tipo de módulo y dependencias con su rango de versión
pnpm-lock.yaml archivo de bloqueo: la versión exacta de cada dependencia y de sus dependencias
pnpm-workspace.yaml raíz de instalación propia, y la prohibición de ejecutar scripts al instalar
server.mjs código JavaScript (módulo ES)

🔧 Angular#

Framework completo con contenedor de dependencias, enrutado, formularios y herramientas en la caja. La opinión arquitectónica más fuerte del ecosistema JavaScript.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts
pnpm exec tsc -p tsconfig.json

Arrancarla suelta, sin el verificador:

PORT=3000 node dist/main.js

Qué hay dentro de su directorio:

Archivo Qué es
ejecutar.json la receta que usa el verificador: qué hace falta, cómo se prepara y cómo arranca
package.json manifiesto de Node.js: nombre, tipo de módulo y dependencias con su rango de versión
pnpm-lock.yaml archivo de bloqueo: la versión exacta de cada dependencia y de sus dependencias
pnpm-workspace.yaml raíz de instalación propia, y la prohibición de ejecutar scripts al instalar
src/enrutador.ts código TypeScript
src/main.ts código TypeScript
tsconfig.json configuración del compilador de TypeScript

🔧 Svelte#

Mueve el trabajo del navegador al compilador. Cambia el coste de ejecución por dependencia de la fase de construcción: un compromiso, no una mejora gratuita.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts

Arrancarla suelta, sin el verificador:

PORT=3000 node server.mjs

Qué hay dentro de su directorio:

Archivo Qué es
Pantalla.svelte componente de Svelte
ejecutar.json la receta que usa el verificador: qué hace falta, cómo se prepara y cómo arranca
enrutador.mjs código JavaScript (módulo ES)
package.json manifiesto de Node.js: nombre, tipo de módulo y dependencias con su rango de versión
pnpm-lock.yaml archivo de bloqueo: la versión exacta de cada dependencia y de sus dependencias
pnpm-workspace.yaml raíz de instalación propia, y la prohibición de ejecutar scripts al instalar
server.mjs código JavaScript (módulo ES)

Si alguna cadena de herramientas no está en tu máquina, node scripts/doctor.mjs dice cuál falta y con qué comando se instala. No hace falta tenerlas todas: el verificador ejecuta lo que encuentra y declara lo que omitió.

<!-- fin generado: fichas -->

🌐 Las implementaciones — el código a la vista#

Tres comparten el mismo enrutador — enrutador.mjs— y Angular lo reescribe en TypeScript.

 * Eso es todo. React Router, Vue Router, el enrutador de Angular y el de
 * SvelteKit añaden navegación sin recarga, carga perezosa, guardias y
 * transiciones — pero por debajo, lo que deciden qué se pinta son estas veinte
 * líneas.

El orden, que es la trampa de la clase:

export const RUTAS = [
  { patron: "/", pantalla: "inicio" },
  { patron: "/tareas", pantalla: "listado" },
  { patron: "/tareas/nueva", pantalla: "nueva" },
  { patron: "/tareas/:id", pantalla: "detalle" },
];
 * `/tareas/nueva` va ANTES que `/tareas/:id`, porque si no, «nueva» se comería
 * el hueco del identificador y siempre ganaría el detalle. Es la misma decisión
 * que la clase 012 tomó en el servidor, con la diferencia de que allí algunos
 * frameworks ordenan por especificidad y aquí lo ordenas tú.

El emparejador, y la línea que evita el fallo silencioso:

  // Distinto número de segmentos es «no coincide». Sin esta línea, `/tareas`
  // emparejaría con `/tareas/:id` y el parámetro llegaría vacío.
  if (esperados.length !== recibidos.length) return { coincide: false, parametros: {} };

React · react/server.mjs#

 * En React Router esto se escribiría como `<Route path="/tareas/:id"
 * element={<Detalle />} />` y el parámetro se leería con `useParams()`. La
 * mecánica es la misma: la tabla dice qué componente, el emparejador dice con
 * qué datos.

Y la simetría que hace posible todo lo demás:

  // CUALQUIER OTRA DIRECCIÓN PASA POR EL ENRUTADOR.
  //
  // En un cliente, esto lo hace el navegador sin pedir nada al servidor. Aquí lo
  // hace el servidor con la misma tabla — y esa simetría es la que permite que
  // una aplicación renderice en los dos sitios.
        biblioteca_habitual: "React Router, o el enrutador de un metaframework",
        viene_en_el_nucleo: false,

Vue · vue/server.mjs#

 * La diferencia de ecosistema es que Vue Router es **oficial**: lo mantiene el
 * mismo equipo, la documentación lo da por hecho y no hay dos opciones
 * compitiendo. Es una decisión menos que tomar, y una dependencia igual de real.

Con un detalle de comportamiento que conviene saber:

        el_orden_lo_decide: "Vue Router ordena por especificidad, no por el orden de la tabla",

Vue Router no depende de cómo ordenes la tabla: pone las literales por delante solo. Es la misma diferencia que la clase 005 encontró entre Spring y Express.

Angular · angular/src/enrutador.ts#

El único de los cuatro con enrutador en el framework:

 * En Angular esa tabla existe de verdad y tiene un tipo: `Routes`, del paquete
 * `@angular/router`, que **viene en el framework**. Es el único de los cuatro
 * que no obliga a elegir biblioteca.

Y con la tabla real escrita al lado, para que se vea el parecido:

 *   const rutas: Routes = [
 *     { path: "", component: Inicio },
 *     { path: "tareas/nueva", component: Nueva },
 *     { path: "tareas/:id", component: Detalle },
 *     { path: "**", component: NoEncontrada },
 *   ];
 * Aquí se reescribe la lógica a mano para que el emparejamiento se vea, que es
 * lo que la clase enseña. Y hay una diferencia de comportamiento que sí importa:
 * **el enrutador de Angular empareja en orden**, como esta versión, así que
 * poner `tareas/:id` antes que `tareas/nueva` rompe la aplicación igual.

Y un detalle de plantilla que resuelve el caso de la pantalla sin identificador — angular/src/main.ts:

    // `[attr.data-id]` con `null` NO escribe el atributo, que es justo lo que se
    // quiere: la pantalla de inicio no tiene identificador.

Svelte · svelte/Pantalla.svelte#

  // En SvelteKit no hay tabla que escribir: **las rutas son directorios**.
  // `src/routes/tareas/[id]/+page.svelte` es la ruta `/tareas/:id`, y el
  // parámetro llega en `data`. Es la convención sobre la configuración llevada
  // al enrutado, y es la diferencia más grande con los otros tres.
  // La tabla de este archivo existe porque la clase no usa SvelteKit: enseña el
  // modelo, no el metaframework. Cómo se comporta el metaframework encima es la
  // parte 7.

🔬 Comparación#

Quién trae el enrutador Quién decide el orden Dónde vive la tabla
React una biblioteca que eliges tú, con el orden de la tabla en el código
Vue Vue Router, oficial el router, por especificidad en el código
Angular el framework tú, con el orden de la tabla en el código, con tipo
Svelte SvelteKit el framework, por el nombre en los directorios

Cuatro lecturas:

⚠️ Errores frecuentes#

✅ Verificación#

node scripts/run-class.mjs 090

Y para jugar con el emparejador:

curl -s "http://127.0.0.1:4100/emparejar?patron=/a/:x/b/:y&ruta=/a/1/b/2"

🧪 Reto de transferencia#

  1. Invierte el orden de /tareas/nueva y /tareas/:id en la implementación de React y ejecuta la clase. El caso 3 se pone en rojo, y ese rojo es un fallo real que en producción nadie nota hasta que alguien pulsa «nueva».
  2. Comprueba el 404 de tu aplicación. Pide una dirección inventada y mira el estado de la respuesta, no la pantalla.
  3. Busca estado que debería estar en la dirección. Filtros, pestañas, paginación. Cada uno que muevas a la URL es uno que el usuario puede compartir.

🔗 Enlaces#

Fuentes#