mcpField
T
tanstack-vue-router-skilld

tanstack-vue-router-skilld

github.com/skilld-dev开发18499 次安装3 周前更新
代码仓库

关于 tanstack-vue-router-skilld

tanstack-vue-router-skilld 是面向使用 @tanstack/vue-router 构建 Vue 应用的开发者参考技能,涵盖类型安全路由、导航、数据加载、搜索参数和 SSR。

主要功能

  1. 01类型安全的文件式与代码式路由
  2. 02Vue 组合式函数和路由组件
  3. 03加载器、上下文与重定向
  4. 04搜索参数验证
  5. 05SSR 入口与组件

适用场景

  1. 01构建类型安全的 Vue 导航
  2. 02通过身份验证检查加载路由数据
  3. 03验证并转换搜索参数
  4. 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.md
composables.md
data-loading.md
file-based-routing.md
navigation-links.md
routes.md
search-params.md
ssr.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/...)

硬性规则

  1. 大多数组合式函数返回 Ref<T>——在脚本中使用 .value;模板会自动解包。useRouter()、useNavigate()、useLinkProps()、useAwaited() 不会返回 ref。完整表格见 references/composables.md。
  2. 绝不要强制转换或标注推断出的路由/路由器类型。 类型来自路由树和 Register 声明。
  3. 注册路由器以确保类型安全——否则,Link/useNavigate/useSearch 会接受任意字符串:
declare module '@tanstack/vue-router' {
  interface Register {
    router: typeof router
  }
}
  1. 不是 vue-router。 绝不要从 vue-router 导入 useRoute/useRouter,也不要使用 <router-view>/<router-link>。
  2. beforeLoad/loader 是普通异步函数。 Vue 组合式 API(ref、computed、生命周期钩子)不能在其中运行。通过路由器 context 传递状态。
  3. 路由选项接受 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、getRouteApisrc/route.ts:95、src/route.ts:220、src/route.ts:460、src/fileRoute.ts:57
rootRouteWithContextcreateRootRouteWithContextsrc/route.ts:414
NotFoundRoute / routerOptions.notFoundRoute路由选项 notFoundComponent / 路由器选项 defaultNotFoundComponenthttps://tanstack.com/router/latest/docs/framework/vue/guide/not-found-errors
<ScrollRestoration />createRouter 中的 scrollRestoration: truesrc/ScrollRestoration.tsx:18
beforeLoad/loader 中的 opts.navigatethrow redirect({ to })https://tanstack.com/router/latest/docs/framework/vue/api/router/RouteOptionsType
parseParams/stringifyParamsparams.parse/params.stringify同一份 RouteOptionsType 文档
preSearchFilters/postSearchFilterssearch.middlewares同一份 RouteOptionsType 文档
FileRouteLoader / 单独的 .lazy.ts 加载器文件将加载器保留在路由文件中src/fileRoute.ts:153
useBlocker(fn, condition) 旧版签名{ shouldBlockFn } 对象src/useBlocker.tsx:139-151

最佳实践

参考资料

评论

支持 Markdown