Миграция с vk-mini-apps-router
Новый роутер даёт feature parity со старым @vkontakte/vk-mini-apps-router, но API другой: кодовые маршруты TanStack, типизированная навигация по зарегистрированному дереву, VKUI рендерится хостами пакета. Ниже — маппинг по кускам.
Установка и сетап
Заголовок раздела «Установка и сетап»// старыйimport { createHashRouter, RouterProvider } from "@vkontakte/vk-mini-apps-router";const router = createHashRouter(routes);
// новыйimport { RouterProvider, createRouter } from "@tanstack/react-router";import { createApp, createVkHistory, NotFound } from "@vkma.dev/router/preset";
const app = createApp({ views: { main: { panels: { index: { path: "/", component: HomePanel }, list: { path: "/list", component: ListPanel }, }, }, },});
const router = createRouter({ routeTree: app.routeTree, history: createVkHistory(), defaultNotFoundComponent: NotFound,});createVkHistory() заменяет все три старые фабрики:
createVkHistory(); // = старый createHashRouter (#/path, дефолт)createVkHistory({ mode: "hash-param" }); // = старый createHashParamRouter (#?path=…)// старый createBrowserRouter = дефолтная история TanStack (опусти history)Оба hash-режима разрешают глубокие ссылки, записанные в другом режиме (легаси-толерантность).
Инлайн-форма views: Record<string, ViewOptions> выше — для маленьких приложений: она даёт типизированную карту app.panels. Для реальных приложений рекомендованы хабы defineView — см. вьюхи и панели.
Конфиг маршрутов → билдеры
Заголовок раздела «Конфиг маршрутов → билдеры»// старый: плоские объекты с метаданными view/panelconst routes = [{ path: "/", panel: "home", view: "main" }, /* … */];
// новый: дерево и есть конфиг; порядок панелей = поток навигации// (цепочка inflate + направление анимации VKUI)createApp({ views: { main: { panels: { index: { component: HomePanel }, // путь "/" list: { path: "/list", component: ListPanel }, item: { path: "/item/$id", component: Item, lazy: routeId => import(/* … */) }, }, }, },});Опции TanStack (loader, beforeLoad, validateSearch, …) проходят в каждый узел нетронутыми.
Навигация
Заголовок раздела «Навигация»// старыйconst routeNavigator = useRouteNavigator();routeNavigator.push({ panel: "persik", params: { id } });routeNavigator.back();
// новый (типизирован зарегистрированным деревом)const navigator = useNavigator();navigator.push({ to: "/item/$id", params: { id } });navigator.back(); // также backToFirst(), replace()Модалки
Заголовок раздела «Модалки»// старый: id модалок в конфиге, showModal пишет history.statecreateModal("persik_modal", "/persik_modal");routeNavigator.showModal("persik_modal");routeNavigator.hideModal();
// новый: модалки — дочерние маршруты панели; цепочки — сиблинги.// Переход к модалке при открытой другой заменяет запись;// хост закрывает старый лист до открытия нового.createPanel("/item/$id", { component: Item, modals: { details: DetailsModal } });navigator.showModal({ to: "/item/$id/details", params: { id } });navigator.hideModal();Глубокие ссылки в пути модалок работают; глобальные модалки ?modal=<id> открываются с любой панели через navigator.showGlobalModal(id) (URL маскируется обратно к пути панели). Подробности — в модалках и popout’ах.
Popout’ы
Заголовок раздела «Popout’ы»// старый: React-элементы через навигаторrouteNavigator.showPopout(<Alert onClose={() => routeNavigator.hidePopout()} />);
// новый: id + сериализуемые props в history.state, компоненты в реестре// (back закрывает popout, props восстанавливаются на back/forward)createApp({ popouts: { confirm: ConfirmAlert } });navigator.showPopout("confirm", { itemId: 42 });navigator.hidePopout();Компонент popout’а получает onClose от хоста — прокинь его в onClosed своего VKUI-компонента.
| Старый | Новый |
|---|---|
useParams({ panel: id }) |
usePanelParams(panelHandle.routeId) (freeze-safe во время back-анимации) |
useSearchParams |
usePanelSearch / TanStack useSearch + validateSearch |
useMetaParams |
location.state (useRouterState) |
useActiveVkuiLocation / useGetPanelForView |
не нужны — VkView ведёт VKUI напрямую; позиция через VKUI useNavId |
useFirstPageCheck |
!router.history.canGoBack() |
useHistoryManager |
внутренний (зеркало панелей); выведем наружу по запросу |
useBlocker / routeNavigator.block |
navigator.block(onLeave) — колбэк получает { currentLocation, nextLocation, action } (back/forward/go = POP); или TanStack useBlocker |
useEnableSwipeBack |
useEnableSwipeBack(enabled, panelRef) — override панели; полный отказ createSwipeBackManager({ enabled: false }) |
RouterLink |
TanStack Link (импорт из @tanstack/react-router) |
Глубокие ссылки и inflate
Заголовок раздела «Глубокие ссылки и inflate»Глубокая ссылка восстанавливает точный экран, включая модалки. При старте не на первой панели поток панелей до неё проигрывается автоматически (замена старого hierarchy + search-параметра inflate). Отключай через createApp({ inflate: false }).
Что не перенесено
Заголовок раздела «Что не перенесено»- Маршрутизация
Root/Epicс мульти-сториями: рендерVkViewна каждую историю внутри собственногоEpic(билдеры и так работают на уровне вьюх). - Пропсы
interval/throttledу RouterProvider: не нужны — панели остаются смонтированными во время переходов в модели статических детей.
История и swipe-back — в истории и хосте.