mcpField
V
virtual-file-routes

virtual-file-routes

github.com/tanstack开发15.3k102 次安装4 天前更新
代码仓库

关于 virtual-file-routes

这是一项用于在代码中定义 TanStack Router 路由树的技能,不必仅依赖文件系统约定。适用于需要明确控制路由结构或混合虚拟路由与物理路由的项目,并提供插件配置和改造检查指引。

主要功能

  1. 01以编程方式定义路由树
  2. 02创建根、索引、分支和布局路由
  3. 03挂载物理路由目录
  4. 04定义虚拟子树
  5. 05配置 Router 插件
  6. 06改动后审查路由树

适用场景

  1. 01在文件约定之外创建路由
  2. 02组合虚拟路由与文件路由
  3. 03将路由目录挂载到 URL 前缀
  4. 04重新生成前检查路由树

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tanstack/router --skill virtual-file-routes

安装后在 Claude Code 中输入/virtual-file-routes确认已加载。

详细介绍

虚拟文件路由(@tanstack/virtual-file-routes)

以编程方式构建路由树,而不依赖文件系统约定。适用于需要显式控制路由结构、混合虚拟路由和物理路由,或在基于文件的路由目录中定义路由子树的场景。

仅当项目配置包含 virtualRouteConfig、虚拟路由配置文件或 __virtual.ts 时,才使用此技能。普通的文件路由变更应使用 Router Core 和 Router Plugin。

关键:类型会被完全推断。绝不要进行类型转换,也不要为推断出的值添加类型注解。

安装

npm install @tanstack/virtual-file-routes

API 参考

rootRoute(file, children?)

创建虚拟路由树的根节点。

import { rootRoute, index, route } from '@tanstack/virtual-file-routes'

const routes = rootRoute('root.tsx', [
  index('index.tsx'),
  route('/about', 'about.tsx'),
])
index(file)

创建索引路由——当父路径完全匹配时默认渲染的路由。

import { index } from '@tanstack/virtual-file-routes'

index('home.tsx')
route(path, ...)

创建路由节点。有三种调用签名:

import { route, index } from '@tanstack/virtual-file-routes'

// Leaf route: path + file
route('/about', 'about.tsx')

// Branch route: path + file + children
route('/dashboard', 'dashboard.tsx', [
  index('dashboard-index.tsx'),
  route('/settings', 'settings.tsx'),
])

// Path prefix only (no file): groups children under a URL segment
route('/api', [route('/users', 'users.tsx'), route('/posts', 'posts.tsx')])
layout(file, children) 或 layout(id, file, children)

创建无路径布局路由——包装子路由,但不添加 URL 路径段。

import { layout, route, index } from '@tanstack/virtual-file-routes'

// ID derived from filename
layout('authLayout.tsx', [
  route('/dashboard', 'dashboard.tsx'),
  route('/settings', 'settings.tsx'),
])

// Explicit ID
layout('admin-layout', 'adminLayout.tsx', [route('/admin', 'admin.tsx')])
physical(pathPrefix, directory) 或 physical(directory)

将基于文件的路由目录挂载到 URL 前缀下。在该目录中使用 TanStack Router 的标准基于文件的路由约定。

import { physical } from '@tanstack/virtual-file-routes'

// Mount posts/ directory under /posts
physical('/posts', 'posts')

// Merge features/ directory at the current level
physical('features')
defineVirtualSubtreeConfig(config)

用于基于文件的路由目录中 __virtual.ts 文件的类型辅助函数。该恒等函数可提供类型推断。

// src/routes/admin/__virtual.ts
import {
  defineVirtualSubtreeConfig,
  index,
  route,
} from '@tanstack/virtual-file-routes'

export default defineVirtualSubtreeConfig([
  index('home.tsx'),
  route('$id', 'details.tsx'),
])

与 Router Plugin 集成

将虚拟路由配置传递给 TanStack Router 插件:

// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
import { routes } from './routes'

export default defineConfig({
  plugins: [
    tanstackRouter({
      target: 'react', // or 'solid', 'vue'
      virtualRouteConfig: routes,
    }),
    // Add your framework's Vite plugin here
  ],
})

或者引用文件路径:

tanstackRouter({
  target: 'react',
  virtualRouteConfig: './routes.ts',
})

完整示例

// routes.ts
import {
  rootRoute,
  route,
  index,
  layout,
  physical,
} from '@tanstack/virtual-file-routes'

export const routes = rootRoute('root.tsx', [
  index('index.tsx'),

  layout('authLayout.tsx', [
    route('/dashboard', 'app/dashboard.tsx', [
      index('app/dashboard-index.tsx'),
      route('/invoices', 'app/dashboard-invoices.tsx', [
        index('app/invoices-index.tsx'),
        route('$id', 'app/invoice-detail.tsx'),
      ]),
    ]),
  ]),

  // Mount file-based routing from posts/ directory
  physical('/posts', 'posts'),
])

常见错误

1. 高:忘记文件路径相对于 routesDirectory

rootRoute、index、route 和 layout 中的文件路径都相对于路由插件中配置的 routesDirectory(默认值:./src/routes)。不要使用绝对路径或相对于项目根目录的路径。

// WRONG — absolute path
route('/about', '/src/routes/about.tsx')

// CORRECT — relative to routesDirectory
route('/about', 'about.tsx')
2. 中:使用 physical() 时目录结构不匹配

传递给 physical() 的目录必须位于 routesDirectory 中,并遵循 TanStack Router 的基于文件的路由约定。

// WRONG — directory doesn't exist or wrong location
physical('/blog', 'src/blog')

// CORRECT — relative to routesDirectory
physical('/blog', 'blog')
// Expects: src/routes/blog/ (with route files inside)
3. 中:混淆 layout() 和 route()

layout() 会创建无路径包装器——它不会添加 URL 路径段。要添加 URL 路径段,请使用 route()。

// This does NOT create a /dashboard URL
layout('dashboardLayout.tsx', [route('/dashboard', 'dashboard.tsx')])

// The URL is /dashboard, and dashboardLayout.tsx wraps it

重构审查

更改虚拟路由树后:

  1. 确认恰好有一个 rootRoute,并且所有引用的文件都相对于 routesDirectory 存在。
  2. 检查每个 route、layout 和 physical 节点是否存在重复的有效路径或 ID。
  3. 确认每个 physical() 目录只挂载一次,且挂载在预期的路径前缀下,并且不会与虚拟子路由重叠。
  4. 重新生成 routeTree.gen.ts,并检查父子关系、完整路径和导入。不要编辑生成的输出。
  5. 更新链接、重定向、参数和 from 类型收窄,然后运行生成器测试、类型测试和生产构建。

评论

支持 Markdown