Доки по разработке
This project is maintained by teniryte
beforeLoad.router.dehydrate() попадает только статика, сами чанки подтянутся при гидрации.import { createLazyRoute } from '@tanstack/react-router'
import { Route as dashboardRoute } from './dashboard'
export const Route = createLazyRoute({
getParentRoute: () => dashboardRoute,
path: 'reports',
pendingComponent: () => <Spinner />, // показывается пока грузится модуль
errorComponent: (props) => <ErrorBox {...props} />,
beforeLoad: async ({ context }) => {
await context.auth.requireRole('analyst')
},
loader: async (opts) => {
const { loader } = await import('./reports.loader')
return loader(opts)
},
component: async () => {
const { ReportsPage } = await import('./reports.component')
return <ReportsPage />
},
})
component и loader могут быть независимыми чанками — полезно, когда нужно прогреть данные заранее через router.loadRoute.pendingComponent и errorComponent хранятся рядом с маршрутом, что избавляет от обязательного Suspense на layout.beforeLoad выполняется до подгрузки модуля компонента. Можно отменить переход, тем самым не тратить трафик.*.route.tsx + *.lazy.tsx: первый описывает статическую часть маршрута, второй экспортирует ленивое содержимое.
```tsx
// settings.lazy.tsx
import { createFileRoute } from ‘@tanstack/react-router’export const Route = createFileRoute(‘/dashboard/settings’).createRoute({
component: () =>
import(‘./SettingsPage’).then((m) =>
- `createFileRoute` автоматически добавляет `Route.lazy()` helper, его можно вызвать для динамического импорта прямо из `routeTree.gen.ts`.
- Не забывайте давать именованные экспорты страницам/loader'ам — tree-shaking эффективнее и не будет промахов с default-export.
## Prefetch и управление чанками
```tsx
<Link
to="/dashboard/reports"
preload="intent"
preloadDelay={120} // предотвращает спам запросов при быстрых hover'ах
preloadGcMaxAge={60_000} // держим модуль в кеше минуту
/>
preload="render" подгрузит маршрут сразу при рендере ссылки (нужно для критичных переходов из меню).router.preloadRoute({ to: '/dashboard/reports', search: { tab: 'weekly' } }) — запускает и beforeLoad, и loader, чтобы данные лежали в routerState.matches.onTouchStart и вызывать router.preloadRoute с задержкой 50–80 мс.router.loadMatches(routes) пригодится на dashboard, где надо прогреть несколько ленивых веток параллельно.chart.js, mapbox) подгружайте внутри loader: const [{ renderChart }, data] = await Promise.all([import('./chart.utils'), fetchData()]).loader + defer() (React 18). Танстековский loader может вернуть defer, а Suspense в layout покажет pendingComponent.beforeLoad ➝ проверка прав без загрузки UI. loader ➝ fetch + dynamic import util-функций. component ➝ только React-дерево, без бизнес-логики.const DashboardShell = () => (
<Suspense fallback={<Spinner />}>
<Outlet />
</Suspense>
)
pendingComponent даст более granular пер-маршрут fallback, а Suspense — общий layout fallback.Route.options.pendingComponent из того же lazy-модуля, чтобы не тащить компоненты в основной бандл.router.dehydrate() не будет встраивать содержимое ленивых чанков — только данные loader’ов. Поэтому клиенту нужен router.hydrate() с тем же routeTree.await router.loadRoute() перед renderToPipeableStream, чтобы скрыть network waterfall после гидрации.startTransition + router.navigate, старайтесь вызвать router.preloadRoute при наведении, иначе Suspense будет мигать после SSR.build.rollupOptions.output.manualChunks → объединяйте редкие admin маршруты в один admin-[hash].js./* webpackChunkName: "reports" */ внутри dynamic import, чтобы DevTools не показывали загадочные id.vite build --analyze или ANALYZE=true next build, чтобы свериться, что ленивые чанки реально отделились.router.devtools есть вкладка Chunks: смотрите, какие маршруты уже загружены, можно руками очищать через router.invalidateRoute.preload="intent" на все Link, которые видны дольше 1 с — пользователи не заметят задержку.const { isFetching } = useRouterState({ select: (s) => s.isFetching }), чтобы показать глобальный progress bar во время ленивой загрузки.createLazyRoute можно обернуть в фабрику, которая принимает зависимости (api, auth).settings/layout.lazy.tsx) — тогда вложенные children будут грузиться мгновенно.routeContext, чтобы подменять импортируемый модуль в runtime.createLazyRoute / .lazy.tsx.pendingComponent + errorComponent для каждого ленивого уровня.beforeLoad, чтобы не качать лишний код.router.loadRoute перед рендером.