Haunted — это легковесная библиотека для создания веб-компонентов с использованием реактивного подхода, вдохновлённого React Hooks. Одним из важных аспектов построения современных веб-приложений является организация навигации. В отличие от императивной навигации через прямое управление историей браузера, декларативная навигация позволяет описывать маршруты как часть структуры приложения, что упрощает поддержку и тестирование.
В Haunted навигация строится с использованием реактивных элементов и состояния. Основная идея — хранить текущий маршрут в состоянии и обновлять отображаемый компонент автоматически при изменении этого состояния.
Простейший пример хранения маршрута:
import { html, component, useState } from 'haunted';
function Router({ routes }) {
const [path, setPath] = useState(window.location.pathname);
window.onpopst ate = () => setPath(window.location.pathname);
const ActiveComponent = routes[path] || routes['/404'];
return html`<${ActiveComponent} />`;
}
customElements.define('my-router', component(Router));
Здесь ключевые моменты:
useState хранит текущий путь.onpopstate синхронизирует состояние с
историей браузера.routes,
где ключи — пути, а значения — компоненты.Такой подход позволяет полностью отказаться от императивного управления DOM и работать через декларативное отображение.
Чтобы навигация была плавной и соответствовала современным
требованиям SPA, важно синхронизировать состояние маршрута с адресной
строкой. Для этого используется метод history.pushState или
history.replaceState.
Пример навигационной ссылки:
function NavLink({ to, children }) {
return html`
<a
href="${to}"
@click=${e => {
e.preventDefault();
history.pushState(null, '', to);
window.dispatchEvent(new Event('popstate'));
}}
>
${children}
</a>
`;
}
Особенности:
preventDefault.pushState.popstate, чтобы компонент
Router обновил текущее состояние.Такой механизм обеспечивает синхронизацию между состоянием приложения и адресной строкой, что является основой декларативной навигации.
Для масштабируемых приложений удобно описывать маршруты как массив объектов с метаданными, компонентами и вложенными путями:
const routes = [
{ path: '/', component: HomePage },
{ path: '/about', component: AboutPage },
{ path: '/users', component: UsersPage, children: [
{ path: '/:id', component: UserProfile }
]
},
{ path: '/404', component: NotFoundPage }
];
Функция поиска подходящего компонента:
function matchRoute(path, routes) {
for (const route of routes) {
if (route.path === path) return route.component;
if (route.children) {
const childMatch = matchRoute(path, route.children);
if (childMatch) return childMatch;
}
}
return routes.find(r => r.path === '/404').component;
}
Преимущества такого подхода:
Haunted поддерживает реактивные эффекты, поэтому можно реализовать
анимацию перехода между маршрутами через хуки useEffect.
Пример:
function Router({ routes }) {
const [path, setPath] = useState(window.location.pathname);
window.onpopst ate = () => setPath(window.location.pathname);
const ActiveComponent = matchRoute(path, routes);
useEffect(() => {
document.body.classList.add('fade-in');
const timeout = setTimeout(() => document.body.classList.remove('fade-in'), 300);
return () => clearTimeout(timeout);
}, [path]);
return html`<${ActiveComponent} />`;
}
Такой подход позволяет поддерживать визуальные переходы без вмешательства в структуру DOM.
В современных приложениях часто используются маршруты с параметрами
(/users/:id). Для их обработки можно использовать простой
парсер:
function parseParams(routePath, actualPath) {
const routeSegments = routePath.split('/');
const pathSegments = actualPath.split('/');
const params = {};
routeSegments.forEach((seg, i) => {
if (seg.startsWith(':')) {
params[seg.slice(1)] = pathSegments[i];
}
});
return params;
}
Применение в компоненте:
const UserProfile = ({ path }) => {
const params = parseParams('/users/:id', path);
return html`<h1>Профиль пользователя ${params.id}</h1>`;
};
Это обеспечивает чистую декларативную работу с динамическими маршрутами без использования сторонних библиотек.
history.pushState и события
popstate обеспечивает синхронизацию с браузером.Такой подход делает навигацию предсказуемой, масштабируемой и удобной для тестирования, полностью соответствуя философии Haunted.