
在 Storybook 中渲染 TanStack Router 嵌套路由树route、path 与 routeOverrides 实战指南【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook本篇技术指南聚焦 Storybook 官方 TanStack React 框架storybook/tanstack-react的核心能力之一——将应用中嵌套的路由树完整搬进 Storybook 的画布。通过parameters.tanstack.router下的route、path、routeOverrides三个参数你可以让某个深层路由例如位于认证外壳内的设置页在 Storybook 中连同其全部父级布局一起渲染并用一行配置“焊死”祖先路由上的守卫与数据加载逻辑。读完本文你将掌握嵌套路由 Story 的标准写法CSF 3 与 CSF Next 两种风格、每个参数的底层行为以及框架复制路由树时的内部原理。一、为什么需要“路由树 Story”在真实的 TanStack Router 应用中页面很少是孤立的路由/settings/profile往往挂在一棵多层的路由树下——__root__ └── /_authenticated 无路径布局认证外壳负责登录态检查 └── /settings └── /profile 真正要展示的页面如果直接把Profile组件塞进 Storybook父级布局提供的 UI 骨架侧边栏、导航、权限上下文就会缺失而如果引入完整应用又会拖入整个运行时。storybook/tanstack-react的解决方案是从你指定的路由出发向上遍历到根路由把整棵路由树复制一份到内存路由器中于是父级布局照常渲染Story 又能独立运行。在动手前请确认项目满足框架的使用前提基于 React≥ 18与 Vite≥ 7构建且安装了tanstack/react-router。二、核心配置三行参数渲染整棵嵌套路由树以下写法直接来自官方文档示例 tanstack-react-route-tree-story.md是渲染嵌套路由的标准姿势。CSF 3 写法// SettingsProfile.stories.ts import type { Meta, StoryObj } from storybook/tanstack-react; // Route 文件本身就是应用路由树的一部分 import { Route } from ./routes/_authenticated/settings/profile; const meta { parameters: { tanstack: { router: { // Storybook 向上遍历到根并复制整棵路由树 // 因此父级布局例如认证外壳也会一并渲染。 route: Route, path: /settings/profile, // 屏蔽父级路由的守卫让 Story 可以独立渲染。 routeOverrides: { /_authenticated: { beforeLoad: () {} }, }, }, }, }, } satisfies Metatypeof Route; export default meta; type Story StoryObjtypeof meta; export const Default: Story {};CSF Next 写法// SettingsProfile.stories.ts import preview from ../.storybook/preview; // Route 文件本身就是应用路由树的一部分 import { Route } from ./routes/_authenticated/settings/profile; const meta preview.meta({ parameters: { tanstack: { router: { // Storybook 向上遍历到根并复制整棵路由树 // 因此父级布局例如认证外壳也会一并渲染。 route: Route, path: /settings/profile, // 屏蔽父级路由的守卫让 Story 可以独立渲染。 routeOverrides: { /_authenticated: { beforeLoad: () {} }, }, }, }, }, }); export const Default meta.story();两种写法只是 Storybook 元数据 API 不同parameters.tanstack.router的对象结构完全一致。三个关键参数分工明确参数类型作用routeAnyRoute \| route options 对象指定要渲染的路由实例框架会自动提取其 React 组件并保留类型化的路由配置pathstring设置 Story 路由器的初始 URL 路径用于在树中定位具体路由routeOverridesPartialRecordstring, RouteOverrideOptions按路由 ID 覆盖树中任意路由的选项如beforeLoad、loader无需修改原始路由对象类型定义见框架源码 routing/types.tsrouteOverrides的键是路由 ID例如/about、/demo/form/simple/$id以及特殊键__root__。三、源码原理Storybook 如何“复制整棵路由树”示例注释里那句“walks up the tree to root and duplicates the full route tree”并非虚言其实现集中在 decorator.tsx 的resolveTree与 duplicate-tree.ts 的duplicateRouteTree中。1. 找到根路由findRootRoute框架先通过findRootRoute(resolvedRoute)沿getParentRoute()向上遍历上限 50 跳以防环直到命中RootRoute实例duplicate-tree.ts。如果传入的route本身就是routeTree.gen.ts导出的整棵RouteTree它会被直接当作根使用。2. 整树复制duplicateRouteTree复制过程不是浅拷贝而是递归重建每一个节点duplicate-tree.ts先用initSourceTree对源树逐节点init()确保派生属性如id、fullPath填充完整对每个子路由用createRoute而非createFileRoute重建避免在全局文件路由注册表中产生重复注册从而规避多 Story 同时挂载时 TanStack 抛出的Duplicate routeIds found: __root__复制过程中同步套用routeOverridescloneChild里读取原路由的options合并对应 override 后生成克隆duplicate-tree.ts根路由也总是新建id与getParentRoute被剥离shellComponentTanStack Start 的html/head/body文档外壳被特意丢弃——因为它无法嵌套进 Story 画布且其 head 内容会劫持页面标题其余根行为component、notFoundComponent、errorComponent、beforeLoad上下文全部保留克隆节点通过byId: Map以原始路由 ID索引这是routeOverrides能按应用中的 ID 定位克隆的关键。3. 定位并注入 StoryresolveStoryLeafinjectStoryComponent复制完成后resolveStoryLeaf依据path可先经params插值、绑定路由 ID 等顺序在克隆树中定位叶子节点duplicate-tree.tsinjectStoryComponent再把Story /注入该叶子的componentdecorator.tsx。最终由createStoryRouter基于createMemoryHistory构建内存路由器decorator.tsx并通过RouterProvider包裹渲染——整个流程零网络请求、零应用 shell 启动。从源码结构还可以推断由于每个 Story 都复制一份独立路由树Story 之间的路由状态天然隔离这也是框架“每个 Story 拥有独立路由器上下文”的架构基础。四、routeOverrides 详解可覆盖哪些路由选项routeOverrides是嵌套路由场景的“安全阀”其可覆盖字段由RouteOverrideOptions接口定义types.ts可覆盖选项说明component覆盖路由组件loader覆盖数据加载函数常用于替换真实 API 调用beforeLoad覆盖进入路由前的守卫/鉴权逻辑本文示例正是用它清空/_authenticated的守卫validateSearch覆盖搜索参数校验loaderDeps覆盖 loader 依赖context覆盖路由上下文配套示例 tanstack-react-route-tree-overrides.md 展示了典型用法用routeOverrides替换loader让 Story 不调用真实接口// UserCard.stories.ts import type { Meta, StoryObj } from storybook/tanstack-react; import { Route } from ./UserCard; const meta { title: Users/UserCard, parameters: { tanstack: { router: { route: Route, params: { userId: 42 }, // 覆盖路由的 loaderStory 不再调用真实 API routeOverrides: { /users/$userId: { loader: async () ({ user: { id: 42, name: Ada Lovelace } }), }, }, }, }, }, } satisfies Metatypeof Route; export default meta; type Story StoryObjtypeof meta; export const Default: Story {};注意两个细节键必须是原始路由 ID。克隆节点自身的idgetter 基于init()填充在注入阶段还未就绪因此框架通过byId映射回原始 ID 才能命中 override相关回归测试见 duplicate-tree.test.ts。__root__可定位根路由。当根路由的beforeLoad注入全局上下文时用routeOverrides: { __root__: { beforeLoad: () ({ ... }) } }即可替换decorator.tsx 与 types.ts 均给出了该键的用法。另外源码在合并 override 时对无路径布局pathless layout显式id而无path做了特殊处理即使 override 给这种布局新增了path也不会触发 TanStack 的 idpath 不变式冲突duplicate-tree.test.ts 有对应验证。五、延伸场景动态参数与无路径布局动态路由参数/$id当目标路由带动态段时用params把参数插值进 URL同时用routeOverrides替换 loader示例见 tanstack-react-dynamic-params.mdimport { Route } from ./$id; const meta { parameters: { tanstack: { router: { route: Route, params: { id: 42 }, routeOverrides: { /showcase/$id: { loader: () ({ item: mockItem }), }, }, }, }, }, } satisfies Metatypeof Route;params的类型会被约束为该路由声明中的参数名例如/$id对应{ id: string }见 types.ts插值逻辑与路径解析在createStoryRouter中通过 TanStack 的interpolatePath完成decorator.tsx。直接挂载无路径布局如果你把 Story 直接绑定在一个无路径布局如_authed/index.tsx上框架的ensureMatchableLeaf会复用该布局已有的 index 子路由path: /或合成一个让布局可被匹配且路径推断落到真实 URLdecorator.tsx。相关回归测试覆盖了“pathless 布局嵌套在 pathful 祖先下”“复用已有 index 子路由”等场景decorator.test.ts。六、验证与调试建议源码单元测试是理解行为的最佳教材duplicate-tree.test.ts 覆盖了 pathless 布局克隆、routeOverrides按原始 ID 生效、lazy 路由绑定迁移、参数透出等decorator.test.ts 验证了createStoryRouter的路径推断与 override 注入。排查问题时可用router.state.matches观察最终匹配的路由 ID 链测试中即用此断言_authed等布局被命中确认父级布局是否真的进入了渲染链。框架的其余参数query搜索参数、context/useRouterContext路由上下文注入等见框架文档 tanstack-react.mdx 的 Parameters 一节可与本文的路由树配置组合使用。小结渲染嵌套路由树是storybook/tanstack-react的核心工作流route提供入口path定位叶子routeOverrides按原始路由 ID 逐个屏蔽守卫与替换加载逻辑框架底层则通过duplicateRouteTree递归复制整棵树并用内存路由器挂载从而在完全不启动应用 shell 的前提下还原真实的嵌套渲染层级。掌握了这套配置与原理你就能为任意深度的 TanStack Router 页面编写独立、可复现的 Story。【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考