关于 virtual-file-routes
这是一项用于在代码中定义 TanStack Router 路由树的技能,不必仅依赖文件系统约定。适用于需要明确控制路由结构或混合虚拟路由与物理路由的项目,并提供插件配置和改造检查指引。
主要功能
- 01以编程方式定义路由树
- 02创建根、索引、分支和布局路由
- 03挂载物理路由目录
- 04定义虚拟子树
- 05配置 Router 插件
- 06改动后审查路由树
适用场景
- 01在文件约定之外创建路由
- 02组合虚拟路由与文件路由
- 03将路由目录挂载到 URL 前缀
- 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
重构审查
更改虚拟路由树后:
- 确认恰好有一个
rootRoute,并且所有引用的文件都相对于routesDirectory存在。 - 检查每个
route、layout和physical节点是否存在重复的有效路径或 ID。 - 确认每个
physical()目录只挂载一次,且挂载在预期的路径前缀下,并且不会与虚拟子路由重叠。 - 重新生成
routeTree.gen.ts,并检查父子关系、完整路径和导入。不要编辑生成的输出。 - 更新链接、重定向、参数和
from类型收窄,然后运行生成器测试、类型测试和生产构建。