Доки по разработке
This project is maintained by teniryte
beforeLoad.router.dehydrate() попадает только статика, сами чанки подтянутся при гидрации.import { createFileRoute } from '@tanstack/react-router'
// routes/dashboard/reports.tsx (critical config)
export const Route = createFileRoute('/dashboard/reports')({
loader: ({ context }) => context.api.reports.list(),
})
import { createLazyFileRoute } from '@tanstack/react-router'
// routes/dashboard/reports.lazy.tsx (non-critical config)
export const Route = createLazyFileRoute('/dashboard/reports')({
component: ReportsPage,
pendingComponent: () => <Spinner />,
errorComponent: (props) => <ErrorBox {...props} />,
})
*.tsx + *.lazy.tsx.beforeLoad и loader остаются в “критическом” файле, чтобы preloading мог стартовать без лишнего waterfall.*.route.tsx + *.lazy.tsx: первый описывает статическую часть маршрута, второй экспортирует ленивое содержимое.
```tsx
// settings.lazy.tsx
import { createLazyFileRoute } from ‘@tanstack/react-router’export const Route = createLazyFileRoute(‘/dashboard/settings’)({
component: SettingsPage,
pendingComponent: () =>
- Для Vite-проектов включайте `autoCodeSplitting: true` в `@tanstack/router-plugin/vite`, чтобы разделение критического/некритического кода происходило автоматически.
- Не забывайте давать именованные экспорты страницам/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, чтобы переход был мгновеннее.onTouchStart и вызывать router.preloadRoute с задержкой 50–80 мс.router.loadRouteChunk(route) (без загрузки данных).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.load() перед 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 во время ленивой загрузки.createLazyFileRoute/createLazyRoute можно обернуть в фабрику.settings/layout.lazy.tsx) — тогда вложенные children будут грузиться мгновенно.routeContext, чтобы подменять импортируемый модуль в runtime.createLazyRoute / .lazy.tsx.pendingComponent + errorComponent для каждого ленивого уровня.beforeLoad, чтобы не качать лишний код.autoCodeSplitting в конфиге плагина.router.load() перед рендером.