关于 tanstack-vue-router-skilld
tanstack-vue-router-skilld 是面向使用 @tanstack/vue-router 构建 Vue 应用的开发者参考技能,涵盖类型安全路由、导航、数据加载、搜索参数和 SSR。
主要功能
- 01类型安全的文件式与代码式路由
- 02Vue 组合式函数和路由组件
- 03加载器、上下文与重定向
- 04搜索参数验证
- 05SSR 入口与组件
适用场景
- 01构建类型安全的 Vue 导航
- 02通过身份验证检查加载路由数据
- 03验证并转换搜索参数
- 04设置服务端渲染
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ skilld-dev/ vue-ecosystem-skills --skill tanstack-vue-router-skilld安装后在 Claude Code 中输入/tanstack-vue-router-skilld确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api-surface.mdcomposables.mddata-loading.mdfile-based-routing.mdnavigation-links.mdroutes.mdsearch-params.mdssr.md详细介绍
@tanstack/vue-router
适用于 Vue 的类型安全路由器。准备的源码:版本 1.170.35(package.json:3)。
- 要求
vue >= 3.3.0(peer),Node>= 20.19(package.json:59-88) - 运行时依赖:
@tanstack/[email protected]、@tanstack/[email protected]、@tanstack/vue-store@^0.11.0 - 仅支持 ESM(
"type": "module"),没有main/CJS 入口 - 入口点:
@tanstack/vue-router、@tanstack/vue-router/ssr/server、@tanstack/vue-router/ssr/client(package.json:29-50)
文档:https://tanstack.com/router(已验证框架页面位于 /router/latest/docs/framework/vue/...)
硬性规则
- 大多数组合式函数返回
Ref<T>——在脚本中使用.value;模板会自动解包。useRouter()、useNavigate()、useLinkProps()、useAwaited()不会返回 ref。完整表格见 references/composables.md。 - 绝不要强制转换或标注推断出的路由/路由器类型。 类型来自路由树和
Register声明。 - 注册路由器以确保类型安全——否则,
Link/useNavigate/useSearch会接受任意字符串:
declare module '@tanstack/vue-router' {
interface Register {
router: typeof router
}
}
- 不是
vue-router。 绝不要从vue-router导入useRoute/useRouter,也不要使用<router-view>/<router-link>。 beforeLoad/loader是普通异步函数。 Vue 组合式 API(ref、computed、生命周期钩子)不能在其中运行。通过路由器context传递状态。- 路由选项接受 Vue SFC:
component、errorComponent、notFoundComponent、pendingComponent和路由器的default*Component选项可使用.vue文件或h()风格的函数组件(src/route.ts:54-58、src/router.ts:26-65)。
使用 Vite 设置(基于文件的路由)
npm install @tanstack/vue-router
npm install -D @tanstack/router-plugin @vitejs/plugin-vue @vitejs/plugin-vue-jsx
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({ target: 'vue', autoCodeSplitting: true }), // before vue()
vue(),
vueJsx(), // needed for .tsx route files
],
})
来源:官方 Vue 示例,https://github.com/TanStack/router/blob/main/examples/vue/basic-file-based-sfc/vite.config.ts
// src/main.ts
import { createApp, h } from 'vue'
import { RouterProvider, createRouter } from '@tanstack/vue-router'
import { routeTree } from './routeTree.gen'
const router = createRouter({
routeTree,
defaultPreload: 'intent',
scrollRestoration: true,
})
declare module '@tanstack/vue-router' {
interface Register {
router: typeof router
}
}
createApp({ setup: () => () => h(RouterProvider, { router }) }).mount('#app')
文件命名、拆分文件约定(.route.ts、.component.vue、.lazy.ts)以及生成的 routeTree.gen.ts:见 references/file-based-routing.md。
基于代码的路由
import { h } from 'vue'
import { createRootRouteWithContext, createRoute, createRouter, Outlet } from '@tanstack/vue-router'
interface RouterContext { auth: { isAuthenticated: boolean } }
const rootRoute = createRootRouteWithContext<RouterContext>()({ component: () => h(Outlet) })
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
loader: ({ context }) => fetchPosts(context.auth),
})
const routeTree = rootRoute.addChildren([postsRoute])
const router = createRouter({
routeTree,
context: { auth: { isAuthenticated: false } },
})
路由选项、路由对象上的钩子以及属性顺序规则:见 references/routes.md。
组合式函数
| 组合式函数 | 返回值 | 备注 |
|---|---|---|
useRouter() | 路由器实例 | 不是 ref |
useRouterState({ select }) | Ref<T> | 传入 select 以避免重新渲染 |
useNavigate({ from? }) | 函数 | 不是 ref |
useSearch({ from }) | Ref<T> | 除非 from 匹配,否则会抛出异常 |
useParams({ from }) | Ref<T> | |
useMatch({ from }) | Ref<T> | |
useLoaderData({ from }) | Ref<T> | |
useLoaderDeps({ from }) | Ref<T> | |
useRouteContext({ from }) | Ref<T> | |
useLocation() | Ref<ParsedLocation> | |
useMatches() / useParentMatches() / useChildMatches() | Ref<Array<Match>> | 接受 select |
useMatchRoute() | 函数 | 调用后返回 Ref<false | params>(src/Matches.tsx:148-172) |
useLinkProps(options) | 链接属性对象 | 用于自定义锚点 |
useBlocker({ shouldBlockFn }) | void 或 Ref<BlockerResolver> | 仅在 withResolver: true 时返回 resolver |
useCanGoBack() | Ref<boolean> | location.state.__TSR_index !== 0(src/useCanGoBack.ts:4-10) |
useAwaited({ promise }) | [data, promise] 元组 | 用于延迟数据 |
路由对象和 getRouteApi('/path') 会提供相同的预设作用域钩子:Route.useSearch()、Route.useLoaderData()、Route.useNavigate()、Route.Link(src/route.ts:72-80)。
详情以及 select/strict 语义:见 references/composables.md。
组件
| 组件 | 用途 |
|---|---|
<RouterProvider :router="router" /> | 挂载路由器;额外属性会更新路由器选项(src/RouterProvider.tsx:60-95) |
<Link to="..." :params="..." :search="..."> | 类型安全的锚点;激活状态会设置 data-status="active" + aria-current="page"(src/link.tsx:522-525);默认插槽接收 { isActive }(src/link.tsx:918) |
<Outlet /> | 渲染匹配的子路由(src/Match.tsx:275) |
<Navigate to="..." /> | 声明式重定向,在 onMounted 中触发(src/useNavigate.tsx:24-40) |
<MatchRoute to="..." :fuzzy="true"> | 匹配时渲染插槽;作用域插槽接收参数 |
<Await :promise="p"> | 使用 <Suspense> 处理延迟数据(src/awaited.tsx:26-43) |
<Block :should-block-fn="fn"> | 阻止导航;作用域插槽接收解析器(src/useBlocker.tsx:470-491) |
<CatchBoundary> / <ErrorComponent> | 通过 onErrorCaptured 实现错误边界 |
<ClientOnly> | 仅在挂载后渲染子项 |
<HeadContent>、<Scripts>、<Html>、<Body>、<Asset>、<ScriptOnce> | SSR 文档外壳 |
链接选项、activeProps(默认为 { class: 'active' },src/link.tsx:477)、预加载(intent/viewport/render)、linkOptions() 和 createLink():references/navigation-links.md。
数据加载和重定向
const route = createFileRoute('/posts/$postId')({
beforeLoad: async ({ context, params }) => {
if (!context.auth.isAuthenticated) {
throw redirect({ to: '/login', search: { from: params.postId } })
}
},
loaderDeps: ({ search: { page } }) => ({ page }),
loader: ({ deps, params }) => fetchPost(params.postId, deps.page),
pendingComponent: PendingSpinner, // .vue file works too
})
throw redirect()、isRedirect()、使用 defer() 延迟流式传输,以及上下文类型:references/data-loading.md。
搜索参数
import { z } from 'zod' // or plain validators
const route = createFileRoute('/posts')({
validateSearch: z.object({ page: z.number().default(1) }),
search: {
middlewares: [stripSearchParams({ page: 1 })],
},
})
验证器、输入/输出架构、retainSearchParams/stripSearchParams 和自定义序列化:references/search-params.md。
1.170.x 中的弃用项(请改用)
| 已弃用 | 替代方案 | 依据 |
|---|---|---|
Route、RootRoute、FileRoute、RouteApi 类 | createRoute、createRootRoute、createFileRoute、getRouteApi | src/route.ts:95、src/route.ts:220、src/route.ts:460、src/fileRoute.ts:57 |
rootRouteWithContext | createRootRouteWithContext | src/route.ts:414 |
NotFoundRoute / routerOptions.notFoundRoute | 路由选项 notFoundComponent / 路由器选项 defaultNotFoundComponent | https://tanstack.com/router/latest/docs/framework/vue/guide/not-found-errors |
<ScrollRestoration /> | createRouter 中的 scrollRestoration: true | src/ScrollRestoration.tsx:18 |
beforeLoad/loader 中的 opts.navigate | throw redirect({ to }) | https://tanstack.com/router/latest/docs/framework/vue/api/router/RouteOptionsType |
parseParams/stringifyParams | params.parse/params.stringify | 同一份 RouteOptionsType 文档 |
preSearchFilters/postSearchFilters | search.middlewares | 同一份 RouteOptionsType 文档 |
FileRouteLoader / 单独的 .lazy.ts 加载器文件 | 将加载器保留在路由文件中 | src/fileRoute.ts:153 |
useBlocker(fn, condition) 旧版签名 | { shouldBlockFn } 对象 | src/useBlocker.tsx:139-151 |
最佳实践
- 在路由器上设置
defaultPreload: 'intent';预加载的数据会被缓存(默认 30 s,defaultPreloadMaxAge)— https://tanstack.com/router/latest/docs/framework/vue/guide/preloading - 在
loaderDeps中仅选择加载器会用到的搜索参数;展开整个search会导致每次无关参数变化时都重新运行加载器 — https://tanstack.com/router/latest/docs/framework/vue/guide/data-loading - 在深层组件中使用
getRouteApi('/posts/$postId'),而不是导入Route对象(可避免循环导入)— https://tanstack.com/router/latest/docs/framework/vue/guide/data-loading - 在
beforeLoad错误处理器中重新抛出错误时,先检查isRedirect(error),以免重定向被吞掉 — https://tanstack.com/router/latest/docs/framework/vue/guide/authenticated-routes - 路由选项的顺序会影响类型推断(
validateSearch/params放在loaderDeps之前,beforeLoad放在loader之前);create-route-property-orderESLint 规则支持自动修复 — https://tanstack.com/router/latest/docs/framework/vue/eslint/create-route-property-order - 对可复用的导航目标,优先使用
linkOptions({ to, ... }),而不是直接使用对象字面量;它适用于<Link>、navigate()、redirect()— https://tanstack.com/router/latest/docs/framework/vue/guide/link-options - 若钩子中的
select返回新对象,请在路由器上启用defaultStructuralSharing: true— https://tanstack.com/router/latest/docs/framework/vue/guide/render-optimizations - 为防止 XSS,协议不在
protocolAllowlist(默认值为DEFAULT_PROTOCOL_ALLOWLIST)中的外部链接会被拦截(src/link.tsx:543-550)
参考资料
- references/api-surface.md — 按类别列出所有公共导出
- references/composables.md — 钩子签名和
Ref语义 - references/routes.md — 路由创建、选项和路由钩子
- references/navigation-links.md —
Link、导航、阻止和预加载 - references/data-loading.md — 加载器、上下文、重定向和延迟数据
- references/search-params.md — 验证和中间件
- references/file-based-routing.md — Vite 插件、命名和代码拆分
- references/ssr.md — 服务端入口点和文档外壳组件