FAST Element — это библиотека для создания веб-компонентов с современным подходом к реактивности и декларативной разметке. В веб-приложениях часто возникает необходимость реализовать клиентскую маршрутизацию — управление переходами между различными представлениями без перезагрузки страницы. FAST Element не содержит встроенного роутера, но его реактивная архитектура позволяет интегрировать маршрутизацию гибко и эффективно.
Для маршрутизации важно хранить текущее состояние пути приложения. Чаще всего используют объект, который содержит текущий путь и параметры. В FAST Element это реализуется через реактивные свойства компонентов.
import { FASTElement, html, css, observable } from "@microsoft/fast-element";
class RouterState {
@observable currentPath = window.location.pathname;
navigate(path) {
this.currentPath = path;
window.history.pushState({}, "", path);
window.dispatchEvent(new Event("popstate"));
}
}
export const routerState = new RouterState();
@observable позволяет автоматически обновлять
компоненты, которые зависят от currentPath.navigate изменяет путь без перезагрузки страницы
и триггерит событие popstate для синхронизации.Для управления отображением разных компонентов в зависимости от пути создается Router Component:
const routerTemplate = html`
<template>
${x => {
switch (x.path) {
case "/":
return html`<home-page></home-page>`;
case "/about":
return html`<about-page></about-page>`;
default:
return html`<not-found-page></not-found-page>`;
}
}}
</template>
`;
class AppRouter extends FASTElement {
@observable path = routerState.currentPath;
connectedCallback() {
super.connectedCallback();
this.handlePopState = () => this.path = routerState.currentPath;
window.addEventListener("popstate", this.handlePopState);
}
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener("popstate", this.handlePopState);
}
}
AppRouter.define({
name: "app-router",
template: routerTemplate
});
path синхронизируется с
routerState.connectedCallback и disconnectedCallback
обеспечивают корректную регистрацию и удаление слушателей событий.Навигацию можно сделать через стандартные элементы
<a> с обработкой события click:
const navTemplate = html`
<nav>
<a href="/" @click=${x => x.navigate('/')}>Главная</a>
<a href="/about" @click=${x => x.navigate('/about')}>О проекте</a>
</nav>
`;
class AppNav extends FASTElement {
navigate(path) {
event.preventDefault();
routerState.navigate(path);
}
}
AppNav.define({
name: "app-nav",
template: navTemplate
});
event.preventDefault() предотвращает стандартную
перезагрузку страницы.routerState
гарантирует синхронизацию с маршрутизатором.Для динамических маршрутов (/user/:id) необходимо
извлекать параметры из URL. Это можно реализовать через регулярные
выражения:
class Route {
constructor(pattern, component) {
this.pattern = pattern;
this.component = component;
}
match(path) {
const regex = new RegExp(this.pattern.replace(/:\w+/g, "([^/]+)"));
const result = path.match(regex);
if (!result) return null;
const keys = [...this.pattern.matchAll(/:(\w+)/g)].map(m => m[1]);
const params = Object.fromEntries(keys.map((k, i) => [k, result[i + 1]]));
return params;
}
}
pattern содержит путь с параметрами.match возвращает объект параметров или
null, если путь не подходит.FAST Element позволяет создавать вложенные маршруты с помощью компонентов внутри компонентов:
const userTemplate = html`
<template>
<h2>Профиль пользователя: ${x => x.userId}</h2>
<app-router></app-router>
</template>
`;
class UserPage extends FASTElement {
@observable userId;
connectedCallback() {
super.connectedCallback();
const params = routerState.currentPath.match(/\/user\/(\d+)/);
this.userId = params ? params[1] : null;
}
}
UserPage.define({
name: "user-page",
template: userTemplate
});
FAST Element работает с window.history и
popstate для синхронизации состояния. Важно учитывать:
history.pushState добавляет запись в историю без
перезагрузки страницы.history.replaceState заменяет текущую запись.popstate гарантирует реактивное
обновление компонентов при навигации назад/вперед.Для крупных приложений можно подгружать страницы динамически с
помощью import():
async function loadComponent(path) {
switch (path) {
case "/about":
await import("./about-page.js");
break;
case "/user":
await import("./user-page.js");
break;
}
routerState.currentPath = path;
}
Ключевое преимущество FAST Element — реактивность на уровне свойств и данных. Это позволяет:
${x => x.path}.routerState.NotFound.