Наследование контекста

Universal Router в JavaScript предоставляет гибкий механизм маршрутизации с поддержкой контекста, который передается между маршрутами. Контекст (context) — это объект, содержащий данные и состояния, доступные во всех обработчиках маршрутов. Основное преимущество контекста заключается в возможности наследования данных от родительских маршрутов к дочерним, что упрощает управление общими состояниями приложения.


Принцип работы контекста

Каждый маршрут может определять свой обработчик (action) и дополнительные данные. Контекст передается в функцию router.resolve(context), где context может включать такие свойства, как:

const context = {
  user: { id: 1, name: 'Alice' },
  auth: true
};

Если маршрут не модифицирует контекст, он наследует все свойства родительских маршрутов. Это позволяет строить дерево маршрутов, где дочерние узлы используют данные, определённые выше.


Определение маршрутов с наследованием контекста

Маршруты Universal Router описываются в виде массива объектов с полями path, action и children. Пример:

import UniversalRouter from 'universal-router';

const routes = [
  {
    path: '/dashboard',
    action: (context) => {
      context.page = 'Dashboard';
      return context.page;
    },
    children: [
      {
        path: '/stats',
        action: (context) => {
          return `Страница ${context.page} - Статистика`;
        }
      },
      {
        path: '/settings',
        action: (context) => {
          context.settingsLoaded = true;
          return `Страница ${context.page} - Настройки`;
        }
      }
    ]
  }
];

const router = new UniversalRouter(routes);

router.resolve('/dashboard/stats', { user: { id: 1 } })
  .then(result => console.log(result));

В этом примере:

  • Родительский маршрут /dashboard добавляет в контекст page.
  • Дочерний маршрут /stats наследует context.page и использует его без необходимости повторного объявления.
  • Контекст может модифицироваться на каждом уровне, сохраняя доступность предыдущих значений.

Механизм передачи контекста

Universal Router передаёт контекст по цепочке маршрутов следующим образом:

  1. Начальный объект context передаётся в router.resolve.
  2. Для каждого уровня маршрута вызывается action, получающий текущий контекст.
  3. Любые изменения в контексте на текущем уровне доступны дочерним маршрутам.
  4. Если дочерний маршрут не определяет новое значение для свойства, используется значение от родителя.

Это позволяет строить иерархические маршруты с общим состоянием, избегая глобальных переменных.


Использование context для данных авторизации

Контекст идеально подходит для передачи данных, необходимых нескольким маршрутам. Например:

const routes = [
  {
    path: '/app',
    action: (context) => {
      if (!context.auth) throw new Error('Доступ запрещен');
      return 'Главная';
    },
    children: [
      { path: '/profile', action: (ctx) => `Профиль пользователя ${ctx.user.name}` },
      { path: '/messages', action: (ctx) => `Сообщения пользователя ${ctx.user.name}` }
    ]
  }
];

Здесь проверка авторизации происходит один раз на родительском маршруте, а дочерние маршруты используют данные пользователя через наследованный контекст.


Расширение контекста динамически

Контекст может изменяться на лету внутри маршрутов, что делает его гибким инструментом:

const routes = [
  {
    path: '/shop',
    action: (context) => {
      context.cart = [];
      return context.cart;
    },
    children: [
      {
        path: '/add/:item',
        action: (context, { params }) => {
          context.cart.push(params.item);
          return context.cart;
        }
      }
    ]
  }
];

Каждое добавление товара изменяет наследуемый объект cart, который доступен на всех дочерних уровнях маршрутов.


Советы по использованию наследования контекста

  • Избегать глобальных переменных: контекст позволяет хранить состояние внутри маршрутов, делая приложение более модульным.
  • Минимизировать мутации: хотя контекст можно изменять на каждом уровне, рекомендуется избегать чрезмерных изменений, чтобы не нарушить предсказуемость данных.
  • Использовать для авторизации и данных сессии: контекст идеально подходит для хранения информации о пользователе, правах доступа и общих настройках.
  • Комбинировать с асинхронными action: Universal Router поддерживает Promise, что позволяет загружать данные на родительском уровне перед передачей их дочерним маршрутам.

Иерархическая передача контекста с children и parentContext

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

const routes = [
  {
    path: '/main',
    action: (context) => {
      context.theme = 'dark';
      return context;
    },
    children: [
      {
        path: '/section',
        action: (context, { parentContext }) => {
          return `Тема приложения: ${parentContext.theme}`;
        }
      }
    ]
  }
];

Поле parentContext предоставляет неизменённый объект родителя, что помогает избегать конфликтов при динамическом изменении контекста на дочерних маршрутах.


Заключение по наследованию контекста

Наследование контекста в Universal Router делает маршрутизацию структурированной и предсказуемой. Контекст обеспечивает:

  • централизованное хранение данных для маршрутов,
  • автоматическое наследование свойств от родителя к детям,
  • гибкость в динамическом изменении состояния маршрутов.

Использование этого механизма позволяет создавать масштабируемые и легко поддерживаемые приложения с чистой иерархией маршрутов.