Миграция с других роутеров

Отличия Universal Router от традиционных роутеров

Universal Router в JavaScript использует подход на основе промисов и динамической маршрутизации, что принципиально отличается от классических роутеров типа react-router или vue-router. Основные отличия:

  • Отсутствие привязки к конкретному фреймворку. Universal Router работает как в браузере, так и в Node.js, что позволяет использовать единый маршрутизатор для серверного рендеринга и клиентской навигации.
  • Динамическое разрешение маршрутов. Вместо заранее определённых компонент маршруты могут возвращать промисы, функции или любой асинхронный контент.
  • Маршрутизация по объектной структуре. В отличие от декларативных <Route> или routes: [], маршруты задаются как массив объектов с path, action, children.

Структура маршрута

Каждый маршрут в Universal Router имеет стандартную структуру:

const routes = [
  {
    path: '/users/:id',
    action: ({ params, query }) => {
      return fetchUser(params.id);
    }
  },
  {
    path: '/about',
    action: () => 'Страница о приложении'
  }
];

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

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

Переход с React Router

React Router использует JSX-компоненты и декларативные маршруты:

<Route path="/users/:id" element={<User />} />

Для миграции на Universal Router:

  1. Конвертация <Route> в объект:

    • path берётся напрямую.
    • element заменяется на action, которая возвращает компонент или данные.
  2. Асинхронные данные: если компонент в React Router загружал данные через useEffect, в Universal Router это можно делать прямо в action с промисами.

Пример миграции:

// React Router
<Route path="/users/:id" element={<User />} />

// Universal Router
{
  path: '/users/:id',
  action: async ({ params }) => {
    const user = await fetchUser(params.id);
    return renderUserComponent(user);
  }
}

Переход с Vue Router

Vue Router использует объект маршрутов с компонентами:

const routes = [
  { path: '/home', component: Home },
  { path: '/profile/:id', component: Profile }
];

Особенности миграции:

  • component заменяется на функцию action.
  • Вложенные маршруты Vue (children) легко конвертируются в children Universal Router.
  • Можно использовать ленивую загрузку компонентов через промисы:
{
  path: '/profile/:id',
  action: async ({ params }) => {
    const Profile = (await import('./Profile.js')).default;
    return Profile(params.id);
  }
}

Обработка ошибок и редиректов

В React и Vue Router часто используются <Navigate> или next({ name: 'home' }). В Universal Router редиректы реализуются через throw { redirect: '/path' }:

{
  path: '/private',
  action: ({ user }) => {
    if (!user.isLoggedIn) {
      throw { redirect: '/login' };
    }
    return 'Добро пожаловать';
  }
}

Обработка ошибок делается через блок catch при вызове метода router.resolve():

router.resolve({ pathname: '/private', user })
  .then(result => render(result))
  .catch(error => {
    if (error.redirect) {
      navigateTo(error.redirect);
    } else {
      renderError(error);
    }
  });

Использование параметров и query

Universal Router передаёт параметры и query в объекте:

  • params — соответствует :id в path.
  • query — параметры строки запроса, автоматически парсятся библиотекой при необходимости.

Пример:

{
  path: '/search',
  action: ({ query }) => {
    return searchItems(query.q);
  }
}

Советы по миграции

  1. Сначала конвертировать маршруты в объектную структуру Universal Router.
  2. Вынести асинхронные операции из компонентов в action.
  3. Использовать вложенные маршруты для сохранения иерархии.
  4. Обрабатывать редиректы и ошибки через throw вместо встроенных методов фреймворка.
  5. Переписать переходы навигации через router.resolve() вместо <Link> или router.push().

Пример полной миграции

import UniversalRouter from 'universal-router';
import { fetchUser, fetchPosts } from './api';

const routes = [
  {
    path: '/',
    action: () => 'Главная страница'
  },
  {
    path: '/users/:id',
    action: async ({ params }) => {
      const user = await fetchUser(params.id);
      if (!user) throw { redirect: '/' };
      return user;
    },
    children: [
      {
        path: '/posts',
        action: async ({ params }) => {
          return fetchPosts(params.id);
        }
      }
    ]
  }
];

const router = new UniversalRouter(routes);

router.resolve({ pathname: '/users/123/posts' })
  .then(result => console.log(result))
  .catch(err => console.error(err));

В этом примере показано, как конвертировать вложенные маршруты, использовать асинхронные данные и обрабатывать редиректы в стиле Universal Router, сохранив структуру и функциональность, аналогичную Vue или React Router.