Skip to content

Маршрутизация

PreffX предоставляет лёгкий механизм клиентской маршрутизации на основе сигналов через утилиту url. Вам не нужно импортировать компонент маршрутизатора — вы получаете реактивный ReadonlySignal<URL>, который синхронизируется с местоположением браузера через Navigation API.

Сигнал url

Каждый компонент PreffX получает url во втором аргументе (utils).

СвойствоТипОписание
urlReadonlySignal<URL>Реактивный сигнал, чьё .value является текущим объектом URL. Изменяется при навигации пользователя на страницу того же источника.

Сигнатура типа

typescript
type PreffXUtils = {
    url: ReadonlySignal<URL>;
    // ...
};

Базовая маршрутизация

Используйте url.value для чтения текущего пути, параметров запроса, хеша или любого другого свойства URL. Комбинируйте с computed для реактивного переключения между представлениями.

tsx
import type { PC } from 'preffx';

const HomePage: PC = () => <div>Главная страница</div>;
const ContactsPage: PC = () => <div>Контакты</div>;
const NotFound: PC = () => <div>Страница не найдена</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="/">Главная</a>
                <a href="/contacts">Контакты</a>
            </nav>
            <main>{page}</main>
        </div>
    );
};

Как это работает

  1. Вы рендерите ссылки <a href="/contacts"> как обычно.
  2. Когда пользователь нажимает на ссылку, браузер генерирует событие navigate.
  3. PreffX перехватывает событие (вызывает event.preventDefault()) и обновляет внутренний сигнал url.
  4. Любой computed, зависящий от url.value, пересчитывается, и DOM реактивно обновляется.

Навигационные ссылки

Используйте стандартные элементы <a> с href. PreffX обрабатывает перехват автоматически — не требуется специальный компонент <Link>.

tsx
<nav>
    <a href="/">Главная</a>
    <a href="/about">О нас</a>
    <a href="/contacts">Контакты</a>
    <a href="/products?category=books&sort=asc">Книги</a>
</nav>

Перехватчик PreffX активируется только когда хотя бы один компонент использует сигнал url (отслеживается внутренним счётчиком URL_WATCHERS). Это означает:

  • Если ни один компонент не читает url, клики по ссылкам <a> работают обычным образом (полноценная навигация по странице).
  • Как только компонент обращается к url.value, навигация перехватывается и обрабатывается на стороне клиента.

Чтение частей URL

Поскольку url является ReadonlySignal<URL>, вы можете читать любое свойство встроенного объекта URL:

tsx
const CurrentRoute: PC = (_, { url }) => {
    return (
        <div>
            <p>Путь: {url.value.pathname}</p>
            <p>Запрос: {url.value.search}</p>
            <p>Хеш: {url.value.hash}</p>
            <p>Хост: {url.value.host}</p>
            <p>Источник: {url.value.origin}</p>
        </div>
    );
};

Все эти свойства реактивны — изменения любого из них вызывают пересчёт зависимых сигналов.

Параметры маршрута (строки запроса)

Разбирайте параметры запроса из url.value.searchParams внутри computed:

tsx
import type { PC } from 'preffx';

const ProductList: PC = (_, { computed, url }) => {
    const category = computed(() =>
        url.value.searchParams.get('category') ?? 'все'
    );
    const sort = computed(() =>
        url.value.searchParams.get('sort') ?? 'название'
    );

    return (
        <div>
            <p>Категория: {category}</p>
            <p>Сортировка: {sort}</p>
            <a href="/products?category=books&sort=asc">Книги (возр.)</a>
            <a href="/products?category=books&sort=desc">Книги (убыв.)</a>
        </div>
    );
};

Динамические сегменты (параметры пути)

Поскольку url предоставляет полный объект URL, вы можете разбирать сегменты пути вручную:

tsx
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 пользователя: {userId}</div>;
};

Для более структурированного подхода создайте простой сопоставитель маршрутов:

tsx
import type { PC } from 'preffx';

// Простой шаблон маршрута → извлечение параметров
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>Пользователь {params.id}</div>;

        const postParams = match('/posts/:postId', pathname);
        if (postParams) return <div>Пост {postParams.postId}</div>;

        return <div>Не найдено</div>;
    });

    return (
        <div>
            <nav>
                <a href="/users/42">Пользователь 42</a>
                <a href="/users/7">Пользователь 7</a>
                <a href="/posts/abc">Пост abc</a>
            </nav>
            <main>{route}</main>
        </div>
    );
};

Междоменная навигация

Перехватчик навигации применяется только к навигациям внутри одного источника. Междоменные ссылки (другой протокол, хост или порт) остаются нетронутыми и вызывают полную загрузку страницы как обычно.

tsx
<a href="https://example.com">Внешняя ссылка</a>  <!-- полная навигация -->
<a href="/internal">Внутренняя ссылка</a>         <!-- клиентская навигация -->

Поддержка браузеров

Маршрутизация основана на Navigation API (globalThis.navigation).

Начиная с января 2026 года, эта функция работает на новейших устройствах и версиях браузеров. Эта функция может не работать на старых устройствах или в старых браузерах.

Опубликовано под лицензией Apache License 2.0