Использование с Svelte

Для интеграции Universal Router с Svelte необходимо создать единый объект маршрутизатора, который будет управлять состоянием приложения и определять, какой компонент отображать в зависимости от текущего URL.

// router.js
import UniversalRouter from 'universal-router';
import Home from './pages/Home.svelte';
import About from './pages/About.svelte';
import NotFound from './pages/NotFound.svelte';

const routes = [
  { path: '/', action: () => Home },
  { path: '/about', action: () => About },
  { path: '(.*)', action: () => NotFound }
];

const router = new UniversalRouter(routes);

export default router;

Здесь ключевым является массив routes, где каждый объект содержит:

  • path — строка маршрута. Поддерживаются динамические сегменты и регулярные выражения.
  • action — функция, возвращающая компонент для рендеринга. Можно использовать асинхронные функции для ленивой загрузки страниц.

Создание рендерера маршрутов

Для Svelte необходимо обновлять отображаемый компонент при изменении URL. Для этого используют реактивную переменную:

// App.svelte
<script>
  import { onMount } from 'svelte';
  import router from './router.js';
  import { writable } from 'svelte/store';

  const currentComponent = writable(null);

  async function navigate(path) {
    const Component = await router.resolve({ pathname: path });
    currentComponent.set(Component);
  }

  onMount(() => {
    navigate(window.location.pathname);

    window.addEventListener('popstate', () => {
      navigate(window.location.pathname);
    });
  });
</script>

<svelte:component this={$currentComponent} />

Ключевые моменты:

  • Используется store для отслеживания текущего компонента.
  • Обработчик popstate обеспечивает реакцию на кнопки «назад/вперед» в браузере.
  • Метод router.resolve({ pathname }) возвращает компонент, который должен быть отображен.

Динамические маршруты и параметры

Universal Router позволяет создавать маршруты с параметрами, которые передаются в action:

const routes = [
  {
    path: '/user/:id',
    action: ({ params }) => {
      return User; // Можно передавать params в компонент
    }
  }
];

Чтобы передать параметры в Svelte-компонент:

async function navigate(path) {
  const Component = await router.resolve({ pathname: path });
  const params = router.resolve({ pathname: path }).params || {};
  currentComponent.set({ component: Component, props: params });
}

<svelte:component this={$currentComponent.component} {...$currentComponent.props} />

Так можно использовать динамические сегменты URL внутри компонентов, например, для загрузки данных пользователя по id.

Ленивый импорт компонентов

Для оптимизации можно загружать компоненты по мере необходимости:

const routes = [
  {
    path: '/',
    action: async () => {
      const module = await import('./pages/Home.svelte');
      return module.default;
    }
  },
  {
    path: '/about',
    action: async () => {
      const module = await import('./pages/About.svelte');
      return module.default;
    }
  }
];

Преимущества:

  • Снижение начального размера бандла.
  • Компоненты подгружаются только при переходе на соответствующий маршрут.

Программная навигация

Для перехода между страницами внутри Svelte-компонента используют функцию navigate:

<button on:click={() => navigate('/about')}>О нас</button>

Это обновляет URL и текущий компонент без перезагрузки страницы. Для полной интеграции можно обернуть функцию navigate в store, чтобы любой компонент имел доступ к переходам.

Обработка ошибок и маршрутов 404

Universal Router позволяет определять catch-all маршруты и асинхронно обрабатывать ошибки:

const routes = [
  { path: '/', action: () => Home },
  { path: '/about', action: () => About },
  {
    path: '(.*)',
    action: () => NotFound
  }
];

router.resolve({ pathname: '/non-existent' })
  .catch(error => {
    console.error('Ошибка маршрутизации:', error);
  });

Использование (.*) гарантирует, что все неопределенные маршруты попадут на страницу NotFound, а ошибки при разрешении маршрута можно перехватывать через .catch.

Поддержка вложенных маршрутов

В Svelte можно создавать вложенные маршруты, возвращая объект с children в action:

const routes = [
  {
    path: '/dashboard',
    action: () => import('./pages/Dashboard.svelte').then(m => m.default),
    children: [
      { path: '/stats', action: () => import('./pages/Stats.svelte').then(m => m.default) },
      { path: '/settings', action: () => import('./pages/Settings.svelte').then(m => m.default) }
    ]
  }
];

Для рендеринга вложенных маршрутов создают отдельный компонент <RouterOutlet />, который получает текущий дочерний маршрут и отображает его.

<script>
  import { writable } from 'svelte/store';
  export let child = null;
</script>

{#if child}
  <svelte:component this={child.component} {...child.props} />
{/if}

Итоговая структура

Оптимальная интеграция Universal Router с Svelte предполагает:

  • Отдельный файл router.js с конфигурацией маршрутов.
  • Store для текущего компонента.
  • Асинхронную функцию navigate для изменения маршрутов.
  • Поддержку динамических сегментов и ленивой загрузки компонентов.
  • Catch-all маршрут для обработки 404.
  • Возможность вложенных маршрутов с передачей дочернего компонента через <RouterOutlet />.

Эта схема обеспечивает гибкую, модульную маршрутизацию без сторонних heavy-weight решений и легко расширяется для больших Svelte-приложений.