Feature-based routing

Feature-based routing в TanStack Router представляет собой подход к организации маршрутов в приложении на основе функциональных областей, а не строго по иерархии URL. Такой метод особенно удобен для крупных приложений, где разделение маршрутов по функциям повышает читаемость кода и облегчает поддержку.


Основные принципы

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

  2. Локальные маршруты Маршруты внутри модуля определяются локально и экспортируются как массив или объект. Это позволяет легко подключать их к корневому маршрутизатору, не засоряя глобальное пространство имен.

  3. Lazy Loading и Code Splitting Feature-based routing естественно интегрируется с ленивой загрузкой. Каждый функциональный блок можно загружать динамически при необходимости, что уменьшает размер начального бандла и ускоряет первую загрузку приложения.


Создание feature-модуля

Пример структуры модуля для управления пользователями:

src/features/users/
├── routes.js
├── components/
│   ├── UserList.jsx
│   └── UserDetail.jsx
└── hooks/
    └── useUserData.js

В файле routes.js определяется набор маршрутов для данного блока:

import { createRoute } from '@tanstack/router';
import UserList from './components/UserList';
import UserDetail from './components/UserDetail';

export const usersRoutes = [
  createRoute({
    path: 'users',
    component: UserList,
    children: [
      createRoute({
        path: ':userId',
        component: UserDetail,
      }),
    ],
  }),
];
  • path указывает путь внутри модуля.
  • component привязывает React-компонент к маршруту.
  • children позволяет вложить подмаршруты, не смешивая их с глобальными маршрутами.

Интеграция feature-модулей в роутер

После определения всех feature-модулей их маршруты объединяются в корневом роутере:

import { createRouter } from '@tanstack/router';
import { usersRoutes } from './features/users/routes';
import { productsRoutes } from './features/products/routes';

const router = createRouter({
  routes: [
    ...usersRoutes,
    ...productsRoutes,
  ],
});

export default router;

Такой подход обеспечивает:

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

Lazy Loading feature-модулей

TanStack Router поддерживает асинхронные компоненты, что упрощает интеграцию динамически загружаемых блоков:

import { createRoute } from '@tanstack/router';
import { lazy } from 'react';

const UserList = lazy(() => import('./components/UserList'));
const UserDetail = lazy(() => import('./components/UserDetail'));

export const usersRoutes = [
  createRoute({
    path: 'users',
    component: UserList,
    children: [
      createRoute({
        path: ':userId',
        component: UserDetail,
      }),
    ],
  }),
];

Использование React.Suspense на уровне маршрутизатора позволяет корректно отображать индикатор загрузки для каждого feature-модуля.


Организация стейт-менеджмента

Feature-based routing хорошо сочетается с локальными состояниями модулей. Каждый модуль может иметь собственные хуки и сторы, а глобальное состояние объединяет только критически важные данные.

Пример: useUserData.js внутри модуля пользователей:

import { useState, useEffect } from 'react';
import { fetchUserList, fetchUserById } from './api';

export function useUserList() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    fetchUserList().then(setUsers);
  }, []);
  
  return users;
}

export function useUserDetail(userId) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUserById(userId).then(setUser);
  }, [userId]);

  return user;
}

Локальное управление состоянием упрощает модульное тестирование и уменьшает взаимозависимости между функциональными блоками.


Взаимодействие между feature-модулями

Для передачи данных между модулями можно использовать:

  • глобальный стор (например, Redux, Zustand);
  • контексты React;
  • URL-параметры и query-параметры, определяемые в маршрутах.

Пример передачи состояния через query-параметр:

createRoute({
  path: 'products',
  component: ProductList,
  searchSchema: {
    category: String,
  },
});

В этом случае модуль продуктов может автоматически получать category из URL, сохраняя независимость от других модулей.


Преимущества feature-based routing

  • Улучшенная модульность и читаемость кода.
  • Простое подключение новых функциональных блоков.
  • Легкость интеграции с динамической загрузкой и ленивым импортом.
  • Разделение локального и глобального состояния.
  • Упрощение тестирования и рефакторинга.

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


Feature-based routing в TanStack Router обеспечивает современную, масштабируемую архитектуру маршрутизации, которая делает проект более структурированным, производительным и удобным для командной разработки.