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

Clase 097 — Carga de datos junto a la ruta#

⬅️ Clase 096 · 📚 Parte 7 · 🎓 Clases · 098 ➡️ Parte 7 — Renderizado y full-stack · Nivel 🔴 avanzado · Pista fullstack (Renderizado y full-stack) ✅ Clase construida — 5 implementaciones verificadas contra contrato.json.

🎯 Objetivo#

En la clase 087 los datos se pedían desde el componente, con un efecto, después de montarlo. El orden era: monta, pinta vacío, pide, espera, vuelve a pintar. De ahí sale el estado de carga, el hueco que salta y la pantalla que llega vacía.

Los cinco metaframeworks invierten ese orden: pide, espera, monta, pinta lleno. Esta clase lo demuestra anotando el orden real, y luego mira lo que de verdad separa a los cinco, que no es el orden sino si el framework sabe que eso era una carga de datos.

📚 Resultados de aprendizaje#

Al terminar podrás:

🧩 La situación#

Tres tareas, una lista y una pantalla de detalle. La fuente tarda diez milisegundos a propósito: si cargar no costara nada, daría igual cuándo empezara.

Y hay una tarea que no existe, la 999, porque la mitad de esta clase es qué pasa cuando el dato no está.

🧮 El contrato#

# Petición Qué comprueba
1 GET /tareas la lista llega cargada, con las tres tareas en el HTML
2 GET /tareas/2 el parámetro llega a la carga: sale la 2 y no sale la 1
3 GET /tareas/999 404 de verdad, no un 200 con cara de error
4 GET /orden.json ["carga:inicio", "carga:fin", "render"]
5 GET /orden.json ocurre en el servidor, sin efecto en el cliente
6 GET /orden.json cada uno declara dónde vive su carga y cómo da su 404

El caso 4 es la clase, y es una afirmación sobre el orden de dos cosas que no se ve en el resultado: la lista sale igual de pintada se cargue cuando se cargue. Así que se anota.

        "json_contiene": {
          "secuencia": ["carga:inicio", "carga:fin", "render"],
          "la_carga_empieza_antes_del_render": true
        }

Y el caso 2 lleva un cuerpo_no_contiene que parece de más y no lo es: sin él, una implementación que cargara la lista entera en la pantalla de detalle pasaría igual.

<!-- generado: fichas -->

📖 Las palabras que esta clase define#

Si alguna de estas no te dice nada todavía, esta es la clase donde se aprende. Las definiciones viven en el glosario, que reúne las del programa entero.

Palabra Qué significa
Carga de datos junto a la ruta Declarar qué datos necesita una pantalla al lado de su ruta, no dentro del componente. Permite que el framework los pida en paralelo antes de pintar, en lugar de descubrirlos uno a uno al renderizar.

🧰 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
Next.js react-metaframework de JavaScript/TypeScript (TypeScript) 2016 MIT Vercel
Nuxt vue-metaframework de JavaScript/TypeScript (TypeScript) 2016 MIT proyecto independiente
SvelteKit svelte-metaframework de JavaScript/TypeScript (TypeScript) 2022 MIT proyecto independiente
Remix react-metaframework de JavaScript/TypeScript (TypeScript) 2021 MIT proyecto independiente
Astro web-metaframework de JavaScript/TypeScript (TypeScript) 2021 MIT proyecto independiente

🔧 Next.js#

Convirtió el renderizado en servidor en la opción por omisión del ecosistema React. Su acoplamiento con una plataforma concreta es la dimensión que el módulo 11 obliga a puntuar.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts
pnpm exec next build

Arrancarla suelta, sin el verificador:

PORT=3000 pnpm exec next start -p 3000

Qué hay dentro de su directorio:

Archivo Qué es
app/Lista.jsx componente en JSX
app/Traza.jsx componente en JSX
app/datos.js código JavaScript
app/layout.js código JavaScript
app/orden.json/route.js código JavaScript
app/registro.js código JavaScript
app/tareas/[id]/page.js código JavaScript
app/tareas/page.js código JavaScript

🔧 Nuxt#

El equivalente de Next.js sobre Vue, con un motor de servidor propio reutilizable fuera del framework.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts
pnpm exec nuxt build

Arrancarla suelta, sin el verificador:

PORT=3000 node .output/server/index.mjs

Qué hay dentro de su directorio:

Archivo Qué es
components/Lista.vue archivo del proyecto
components/Traza.vue archivo del proyecto
datos.ts código TypeScript
ejecutar.json la receta que usa el verificador: qué hace falta, cómo se prepara y cómo arranca
nuxt.config.ts código TypeScript
package.json manifiesto de Node.js: nombre, tipo de módulo y dependencias con su rango de versión
pages/tareas/[id].vue archivo del proyecto
pages/tareas/index.vue archivo del proyecto

🔧 SvelteKit#

Enrutado por sistema de archivos y adaptadores de despliegue intercambiables, que es una estrategia de salida incorporada al diseño.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts
pnpm exec vite build

Arrancarla suelta, sin el verificador:

PORT=3000 node build/index.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/app.html plantilla o marcado
src/lib/Lista.svelte componente de Svelte
src/lib/Traza.svelte componente de Svelte
src/lib/datos.js código JavaScript

🔧 Remix#

Apostó por los estándares de la plataforma web —formularios, respuestas, caché— frente a abstracciones propias. Su fusión con React Router es un ejemplo de convergencia entre proyectos.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts
pnpm exec remix vite:build

Arrancarla suelta, sin el verificador:

PORT=3000 pnpm exec remix-serve ./build/server/index.js

Qué hay dentro de su directorio:

Archivo Qué es
app/Lista.jsx componente en JSX
app/Traza.jsx componente en JSX
app/datos.js código JavaScript
app/registro.js código JavaScript
app/root.jsx componente en JSX
app/routes/orden[.]json.js código JavaScript
app/routes/tareas.$id.jsx componente en JSX
app/routes/tareas._index.jsx componente en JSX

🔧 Astro#

Arquitectura de islas: por omisión no envía JavaScript y cada componente interactivo se declara explícitamente. Permite mezclar React, Vue y Svelte en la misma página, lo que lo hace un banco de pruebas ideal para comparar.

Preparar sus dependencias, dentro de su directorio:

pnpm install --silent --ignore-scripts
pnpm exec astro build

Arrancarla suelta, sin el verificador:

PORT=3000 node ./dist/server/entry.mjs

Qué hay dentro de su directorio:

Archivo Qué es
astro.config.mjs código JavaScript (módulo 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/components/Lista.astro archivo del proyecto
src/components/Traza.astro archivo del proyecto
src/datos.js código JavaScript

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#

El cuaderno, idéntico en las cinco#

astro/src/registro.js — y por qué hace falta:

 * La promesa que esta clase tiene que demostrar es esta: **la carga de datos
 * empieza antes de que el componente exista**. Es una afirmación sobre el orden
 * de dos cosas, y el orden no se ve en el resultado: la lista sale igual de
 * pintada se cargue cuando se cargue.

Con un detalle que costaría una tarde encontrar si se olvidara:

/** La función de carga llama a esto al empezar. Sin reiniciar, el cuaderno
 *  arrastraría los eventos de la petición anterior y la secuencia crecería. */
export function reiniciar() {

Y la fuente, que tarda a propósito — astro/src/datos.js:

 * Tarda diez milisegundos a propósito. Una fuente instantánea no serviría para
 * esta clase: si cargar no cuesta nada, da igual cuándo empiece.
 */
export const TAREAS = [
/** Devuelve `null` si no existe. Quien llama decide qué hacer con eso, y ahí es
 *  donde los cinco frameworks se separan: cada uno tiene su forma de convertir
 *  un `null` en un 404 de verdad. */
export async function pedirUnaTarea(id) {

Y el truco que hace comparable la medición — astro/src/components/Traza.astro:

// Se pinta el último a propósito: para cuando le toca, la secuencia ya está
// completa. Es la forma de sacar el cuaderno de bitácora dentro del propio HTML,
// sin que haga falta que el endpoint comparta memoria con la página.

Astro · no hay función de carga, y no hace falta#

astro/src/pages/tareas/index.astro:

// El frontmatter de una página `.astro` se ejecuta entero en el servidor antes
// de que se pinte una sola etiqueta. Esto de aquí ES la carga: un `await` normal,
// arriba, sin nombre especial.

Y aquí está lo que separa a los cinco, dicho en el archivo más simple de todos:

// A cambio, no hay nada que el framework pueda hacer con ella. En SvelteKit o
// Remix la función de carga tiene nombre y firma conocidos, y por eso el
// framework puede llamarla antes de navegar, en paralelo con la de la ruta
// padre, o volver a llamarla al invalidar. Aquí no: es código dentro de la
// página.

Su 404 — [id].astro:

// El parámetro de la ruta llega en `Astro.params`, y la página decide qué hacer
// si no existe. Devolver una `Response` desde el frontmatter corta el
// renderizado y manda ese estado: es la forma que tiene Astro de dar un 404 de
// verdad en lugar de un 200 con cara de error.

Next.js · un await dentro del componente#

nextjs/app/tareas/page.js:

 * No hay `loader`, no hay `load`, no hay `getServerSideProps`. La página es una
 * función `async` y espera. Es lo más parecido a Astro de los cinco, con una
 * diferencia grande: aquí cualquier componente del árbol puede hacer lo mismo
 * —clase 096—, no solo la página.
 * Lo que se pierde a cambio es lo mismo que pierde Astro: el framework no sabe
 * que esto es una carga de datos. No puede llamarla antes de navegar ni
 * ejecutarla en paralelo con la de otra ruta, porque para él es código.

Y su 404, en tareas/[id]/page.js:

 * Es un detalle que separa a los frameworks serios del resto: una pantalla de
 * error con estado 200 miente a los buscadores, a las cachés y a cualquiera que
 * llame a la ruta desde un programa.

SvelteKit · la función que sí tiene nombre#

sveltekit/src/routes/tareas/+page.server.js — la lista de lo que se compra con la ceremonia:

 * `load` no es código dentro de la página: es una función con nombre y firma que
 * el framework conoce. Y como la conoce, puede hacer cosas con ella que en Astro
 * y en Next no son posibles:
 *
 *   - llamarla al pasar el ratón por encima de un enlace, antes de navegar;
 *   - ejecutarla en paralelo con la `load` de la ruta padre, en lugar de en
 *     cadena;
 *   - volver a llamarla cuando algo la invalide, sin recargar la página.

Y su 404 — [id]/+page.server.js:

 * Y `error(404, …)` lanza. SvelteKit lo recoge, pinta su página de error y manda
 * un 404 de verdad. Es la misma idea que `notFound()` en Next y que devolver una
 * `Response` en Astro: tres formas de decir lo mismo.

Nuxt · el punto intermedio, con una clave#

nuxt/pages/tareas/index.vue:

// Es un punto intermedio entre los dos extremos de esta clase. Como en Astro y
// en Next, la carga está dentro de la página; pero a diferencia de ellos, el
// framework SÍ sabe que es una carga: le da una clave, la deduplica, guarda su
// resultado en la carga útil y la puede volver a ejecutar con `refresh()`.

Con el precio de esa clave, que conviene saber antes y no después:

// El precio de esa clave es que hay que inventarla y que sea única. Es la fuente
// de errores más común de esta parte de Nuxt: dos componentes con la misma clave
// comparten dato sin querer.

Y un detalle de su 404 que cuesta descubrir — [id].vue:

// `useRoute().params` trae el parámetro. Y `createError` con `fatal: true` es lo
// que convierte un dato que no existe en un 404 de verdad: sin `fatal`, Nuxt lo
// trataría como un error recuperable y respondería 200.

Remix · el original#

remix/app/routes/tareas._index.jsx:

 * Remix lo llevó a su conclusión: la ruta es la unidad, la ruta declara qué
 * datos necesita, y el framework se encarga de tenerlos antes de renderizar. Sin
 * estado de carga, sin efecto en el cliente, sin componente que se monte vacío.

Y su 404, que es el más corto de los cinco — tareas.$id.jsx:

 * Y el 404 se da lanzando una `Response` de la plataforma web, sin ninguna
 * función del framework de por medio. Es la postura de Remix llevada al detalle:
 * cuando el estándar ya tiene una forma de decir algo, se usa esa.

🔬 Comparación#

Dónde vive la carga ¿Tiene nombre que el framework conozca? Cómo se da un 404
Astro en el frontmatter de la página devolver una Response con estado 404
Next.js dentro del componente, con await notFound(), que lanza
SvelteKit load en +page.server.js error(404, …), que lanza
Nuxt useAsyncData en el componente, con clave createError({ statusCode: 404, fatal: true })
Remix loader en el archivo de la ruta lanzar una Response estándar

Y la secuencia, que sale idéntica en los cinco:

carga:inicio  →  carga:fin  →  render

Cuatro lecturas:

⚠️ Errores frecuentes#

✅ Verificación#

node scripts/run-class.mjs 097

Para verlo tú, con cualquiera arrancada:

curl -s http://127.0.0.1:4100/orden.json

Y la comprobación que hay que llevarse a cualquier proyecto —el 404 de verdad:

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:4100/tareas/999

🧪 Reto de transferencia#

  1. Comprueba tus 404. Pide una ruta con un identificador que no exista en tu aplicación y mira el código de estado, no la pantalla. Si sale 200, tienes un fallo que nadie ha visto porque en el navegador se ve bien.
  2. Pon el cuaderno. Tres líneas —anotar al empezar la carga, al terminar y al renderizar— dicen si tu pantalla carga antes o después de montar. Es la diferencia entre la parte 6 y la 7.
  3. Busca tus cargas anónimas. Cuenta cuántas de tus llamadas a datos el framework puede reconocer como tales. Las que no, no se pueden adelantar, ni paralelizar, ni invalidar.

🔗 Enlaces#

Fuentes#