Enrutamiento
PreffX proporciona un mecanismo ligero de enrutamiento del lado del cliente basado en señales a través de la utilidad url. No hay ningún componente de enrutador que importar — obtienes un ReadonlySignal<URL> reactivo que se mantiene sincronizado con la ubicación del navegador mediante la Navigation API.
La señal url
Cada componente de PreffX recibe url en su segundo argumento (utils).
| Propiedad | Tipo | Descripción |
|---|---|---|
url | ReadonlySignal<URL> | Señal reactiva cuyo .value es el objeto URL actual. Cambia cuando el usuario navega a una página del mismo origen. |
Firma de tipo
type PreffXUtils = {
url: ReadonlySignal<URL>;
// ...
};Enrutamiento básico
Usa url.value para leer el nombre de ruta actual, los parámetros de búsqueda, el hash o cualquier otra propiedad de URL. Combínalo con computed para cambiar reactivamente entre vistas.
import type { PC } from 'preffx';
const HomePage: PC = () => <div>Página de inicio</div>;
const ContactsPage: PC = () => <div>Contactos</div>;
const NotFound: PC = () => <div>Página no encontrada</div>;
export const App: PC = (_, { computed, url }) => {
const page = computed(() => {
switch (url.value.pathname) {
case '/':
return <HomePage />;
case '/contacts':
return <ContactsPage />;
default:
return <NotFound />;
}
});
return (
<div>
<nav>
<a href="/">Inicio</a>
<a href="/contacts">Contactos</a>
</nav>
<main>{page}</main>
</div>
);
};Cómo funciona
- Renderizas enlaces
<a href="/contacts">como de costumbre. - Cuando el usuario hace clic en el enlace, el navegador emite un evento
navigate. - PreffX intercepta el evento (llama a
event.preventDefault()) y actualiza la señal internaurl. - Cualquier
computedque dependa deurl.valuese reevalúa y el DOM se actualiza de forma reactiva.
Enlaces de navegación
Usa elementos <a> estándar con href. PreffX maneja la intercepción automáticamente — no se requiere un componente <Link> especial.
<nav>
<a href="/">Inicio</a>
<a href="/about">Acerca de</a>
<a href="/contacts">Contactos</a>
<a href="/products?category=books&sort=asc">Libros</a>
</nav>El interceptor de PreffX solo se activa cuando al menos un componente está usando la señal url (rastreado por un contador interno URL_WATCHERS). Esto significa:
- Si ningún componente lee
url, los clics en enlaces<a>se comportan normalmente (navegación completa de página). - Tan pronto como un componente accede a
url.value, la navegación es interceptada y manejada del lado del cliente.
Lectura de partes de la URL
Debido a que url es un ReadonlySignal<URL>, puedes leer cualquier propiedad del objeto nativo URL:
const CurrentRoute: PC = (_, { url }) => {
return (
<div>
<p>Ruta: {url.value.pathname}</p>
<p>Búsqueda: {url.value.search}</p>
<p>Hash: {url.value.hash}</p>
<p>Host: {url.value.host}</p>
<p>Origen: {url.value.origin}</p>
</div>
);
};Todos estos son reactivos — los cambios en cualquiera de ellos desencadenan el recálculo de las señales dependientes.
Parámetros de ruta (cadenas de consulta)
Analiza los parámetros de consulta desde url.value.searchParams dentro de un computed:
import type { PC } from 'preffx';
const ProductList: PC = (_, { computed, url }) => {
const category = computed(() =>
url.value.searchParams.get('category') ?? 'todos'
);
const sort = computed(() =>
url.value.searchParams.get('sort') ?? 'nombre'
);
return (
<div>
<p>Categoría: {category}</p>
<p>Ordenar por: {sort}</p>
<a href="/products?category=books&sort=asc">Libros (asc)</a>
<a href="/products?category=books&sort=desc">Libros (desc)</a>
</div>
);
};Segmentos dinámicos (parámetros de ruta)
Dado que url te proporciona el objeto URL completo, puedes analizar los segmentos de ruta manualmente:
import type { PC } from 'preffx';
const UserProfile: PC = (_, { computed, url }) => {
const segments = computed(() => url.value.pathname.split('/').filter(Boolean));
// pathname = "/users/42" → segments = ["users", "42"]
const userId = computed(() => segments.value[1]);
return <div>ID de usuario: {userId}</div>;
};Para un enfoque más estructurado, construye un simple coincidente de rutas:
import type { PC } from 'preffx';
// Patrón de ruta simple → extractor de parámetros
function match(pattern: string, pathname: string): Record<string, string> | null {
const patternParts = pattern.split('/').filter(Boolean);
const pathParts = pathname.split('/').filter(Boolean);
if (patternParts.length !== pathParts.length) return null;
const params: Record<string, string> = {};
for (let i = 0; i < patternParts.length; i++) {
if (patternParts[i].startsWith(':')) {
params[patternParts[i].slice(1)] = pathParts[i];
} else if (patternParts[i] !== pathParts[i]) {
return null;
}
}
return params;
}
const RouteExample: PC = (_, { computed, url }) => {
const route = computed(() => {
const { pathname } = url.value;
const params = match('/users/:id', pathname);
if (params) return <div>Usuario {params.id}</div>;
const postParams = match('/posts/:postId', pathname);
if (postParams) return <div>Publicación {postParams.postId}</div>;
return <div>No encontrado</div>;
});
return (
<div>
<nav>
<a href="/users/42">Usuario 42</a>
<a href="/users/7">Usuario 7</a>
<a href="/posts/abc">Publicación abc</a>
</nav>
<main>{route}</main>
</div>
);
};Navegación entre orígenes
El interceptor de navegación solo se aplica a navegaciones del mismo origen. Los enlaces entre orígenes (diferente protocolo, host o puerto) no se modifican y provocan una carga completa de la página como es normal.
<a href="https://example.com">Enlace externo</a> <!-- navegación completa -->
<a href="/internal">Enlace interno</a> <!-- navegación del lado del cliente -->Soporte del navegador
El enrutamiento depende de la Navigation API (globalThis.navigation).
Desde enero de 2026, esta función funciona en los dispositivos y versiones de navegador más recientes. Es posible que esta función no funcione en dispositivos o navegadores antiguos.