Sistema de Rutas
¿Qué es el sistema de rutas?
El sistema de rutas de TypesCraft proporciona navegación declarativa para aplicaciones de una sola página (SPA). Permite definir rutas, manejar navegación, extraer parámetros de URL y cargar contenido de forma dinámica. Nota: El sistema funciona con el singleton ContextRouter para coordinar la navegación global.
Componentes principales
El sistema de rutas está compuesto por tres clases principales:
- Router - Widget principal que maneja la navegación y renderizado de rutas
- Route - Representa una ruta individual con su patrón y contenido
- Route404 - Ruta especializada para manejar páginas no encontradas
Router
El Router es el widget central que gestiona la navegación de la aplicación. Se integra dentro de una MaterialApp y debe ser retornado desde el método build() de la clase principal.
import Router, { Route, Route404 } from "@seigenta/TypesCraft/widgets/routing/router";
import MaterialApp from "@seigenta/TypesCraft/MaterialApp";
import { Context, Widget } from "@seigenta/TypesCraft/widget";
import { DefaultStyle } from "@seigenta/TypesCraft/styles";
import Color from "@seigenta/TypesCraft/utils/color";
class App extends MaterialApp {
globalStyle = new DefaultStyle({
backgroundColor: Color.white(),
});
build(_: Context): Widget<any> | null {
return new Router({
routes: [
new Route({
path: "",
page: async () => {
const module = await import("./pages/HomePage");
return new module.default({});
},
}),
new Route({
path: "/about",
page: async () => {
const module = await import("./pages/AboutPage");
return new module.default({});
},
}),
new Route404({
page: async () => {
const module = await import("./pages/404");
return new module.default({});
},
}),
],
});
}
}
const app = new App();
app.run();
Parámetros del Router:
- routes: Route[] - Array de instancias Route que definen la estructura de navegación
- key?: string - Identificador único opcional para el widget
Métodos principales:
- getParams() - Obtiene los parámetros de la ruta activa actual
Navegación Programática
Para navegar entre rutas programáticamente, utiliza la función navigateTo() que maneja
automáticamente el historial del navegador y las transiciones de vista:
import { navigateTo } from "@seigenta/TypesCraft/widgets/routing/ContextRouter";
// Navegación básica
navigateTo("/about");
// Navegación con transición de vista (recomendado)
navigateTo("/profile", true);
// Ejemplo en un botón
new Button({
onPressed: () => {
navigateTo("/dashboard", true);
},
child: new Text({ text: "Ir al Dashboard" })
});
La función navigateTo() acepta dos parámetros:
- path: string - La ruta de destino
- viewTransition?: boolean - Si usar transiciones de vista (opcional, recomendado true)
Route
La clase Route representa una ruta individual en la aplicación. Maneja el emparejamiento de patrones, la extracción de parámetros y la carga perezosa del contenido.
import { Route } from "@seigenta/TypesCraft/widgets/routing/router";
import MyPage from "./pages/MyPage";
// Ruta con carga inmediata
new Route({
path: '/home',
page: new MyPage({})
});
// Ruta con carga perezosa (recomendado)
new Route({
path: '/blog/{postId}',
page: async () => {
const module = await import("./pages/BlogPost");
return new module.default({});
}
});
Parámetros de Route:
- path: string - El patrón de ruta que se emparejará contra las URLs
- page: () => Promise<Widget<any>> | Widget<any> - Función que carga el contenido de la página o instancia directa del widget
Patrones de Rutas
El sistema soporta tres tipos de patrones de ruta:
1. Rutas exactas
Emparejan exactamente con la URL especificada:
new Route({
path: '/home', // Empareja solo con '/home'
page: HomePage
});
2. Rutas con parámetros
Capturan segmentos dinámicos de la URL usando la sintaxis {nombreParametro}:
new Route({
path: '/user/{id}', // Empareja '/user/123', '/user/abc', etc.
page: UserProfilePage
});
new Route({
path: '/blog/{category}/{postId}', // Múltiples parámetros
page: BlogPostPage
});
3. Rutas comodín
Emparejan cualquier ruta que comience con el prefijo especificado usando *:
new Route({
path: '/docs/*', // Empareja '/docs/api', '/docs/guide/intro', etc.
page: DocsPage
});
Extracción de Parámetros
Los parámetros extraídos de rutas parametrizadas están disponibles a través de la función getParamsPath()
del ContextRouter, que agrega parámetros de todas las rutas activas:
import { getParamsPath } from "@seigenta/TypesCraft/widgets/routing/ContextRouter";
// Obtener parámetros de la ruta actual
const params = getParamsPath();
// Para la ruta '/user/{id}' visitando '/user/123'
const userId = params.get('id'); // '123'
// Para la ruta '/blog/{category}/{postId}' visitando '/blog/tech/my-post'
const category = params.get('category'); // 'tech'
const postId = params.get('postId'); // 'my-post'
// Ejemplo de uso en un componente
class UserProfile extends Widget<any> {
async _render(): Promise<HTMLElement | null> {
const params = getParamsPath();
const userId = params.get('id');
// Usar el ID para cargar datos del usuario
console.log(`Cargando perfil del usuario: ${userId}`);
return /* tu renderizado aquí */;
}
}
Route404
Route404 es una subclase especializada de Route para manejar casos de "página no encontrada". Debe colocarse como la última ruta en la colección del router.
import { Route404 } from "@seigenta/TypesCraft/widgets/routing/router";
new Router({
routes: [
// ... otras rutas ...
new Route404({
page: async () => {
const module = await import("./pages/NotFoundPage");
return new module.default({});
}
})
]
});
Carga Perezosa (Lazy Loading)
Se recomienda usar carga perezosa para optimizar el rendimiento de la aplicación:
new Route({
path: "/dashboard",
page: async () => {
// El módulo solo se carga cuando se navega a esta ruta
const module = await import("./pages/Dashboard");
return new module.default({});
}
});
Ejemplo Completo
import Router, { Route, Route404 } from "@seigenta/TypesCraft/widgets/routing/router";
import MaterialApp from "@seigenta/TypesCraft/MaterialApp";
import { Context, Widget } from "@seigenta/TypesCraft/widget";
import { DefaultStyle } from "@seigenta/TypesCraft/styles";
import Color from "@seigenta/TypesCraft/utils/color";
import { navigateTo, getParamsPath } from "@seigenta/TypesCraft/widgets/routing/ContextRouter";
// Aplicación completa con sistema de rutas
class App extends MaterialApp {
globalStyle = new DefaultStyle({
backgroundColor: Color.white(),
});
build(_: Context): Widget<any> | null {
return new Router({
routes: [
// Página de inicio (ruta vacía)
new Route({
path: "",
page: async () => {
const module = await import("./pages/HomePage");
return new module.default({});
},
}),
// Páginas estáticas
new Route({
path: "/about",
page: async () => {
const module = await import("./pages/AboutPage");
return new module.default({});
},
}),
new Route({
path: "/contact",
page: async () => {
const module = await import("./pages/ContactPage");
return new module.default({});
},
}),
// Ruta con parámetro único
new Route({
path: "/user/{id}",
page: async () => {
const module = await import("./pages/UserProfile");
return new module.default({});
},
}),
// Ruta con múltiples parámetros
new Route({
path: "/blog/{category}/{postId}",
page: async () => {
const module = await import("./pages/BlogPost");
return new module.default({});
},
}),
// Página 404 (debe ser la última)
new Route404({
page: async () => {
const module = await import("./pages/NotFound");
return new module.default({});
},
})
],
});
}
}
// Inicializar y ejecutar la aplicación
const app = new App();
app.run();
// Ejemplo de navegación desde cualquier parte de la aplicación
class NavigationExample extends Widget<any> {
async _render(): Promise<HTMLElement | null> {
return new Flex({
children: [
new Button({
onPressed: () => navigateTo("/about", true),
child: new Text({ text: "Ir a About" })
}),
new Button({
onPressed: () => navigateTo("/user/123", true),
child: new Text({ text: "Ver Perfil" })
}),
new Button({
onPressed: () => {
const params = getParamsPath();
console.log("Parámetros actuales:", params);
},
child: new Text({ text: "Ver Parámetros" })
})
]
});
}
}
Mejores Prácticas
- Usa carga perezosa - Implementa async/await para cargar páginas solo cuando sea necesario
- Usa navegateTo() - Utiliza la función navigateTo() en lugar de manipular el historial directamente
- Ordena las rutas correctamente - Coloca rutas más específicas antes que las generales
- Incluye Route404 - Siempre proporciona una página de error como última ruta
- Ruta vacía para inicio - Usa path: "" para la página principal en lugar de "/"
- Usa transiciones de vista - Pasa true como segundo parámetro en navigateTo() para mejores transiciones
- Usa parámetros descriptivos - Nombra los parámetros de forma clara ({userId} en lugar de {id})