mcpField
V
vue-router

vue-router

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

关于 vue-router

面向 Vue 应用开发者的 TanStack Router 技能指南,介绍 Vue 绑定、基于文件的路由、类型安全导航与组合式 API,并说明其默认在客户端运行加载器。

主要功能

  1. 01基于文件的路由配置
  2. 02类型安全的链接与导航
  3. 03Vue 路由组合式 API
  4. 04路由数据与搜索参数
  5. 05代码拆分与错误处理

适用场景

  1. 01在 Vue 应用中配置 TanStack Router
  2. 02使用类型化参数构建路由
  3. 03阻止含未保存更改时的导航

安装

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

$npx skills add https://github.com/tanstack/router --skill vue-router

安装后在 Claude Code 中输入/vue-router确认已加载。

详细介绍

Vue Router(@tanstack/vue-router)

此技能基于 router-core。请先阅读 @tanstack/router-core#router-core,了解基础概念。

此技能介绍 TanStack Router 的 Vue 专属绑定、组件、组合式函数和设置方法。

CRITICAL:TanStack Router 的类型完全由推断得出。切勿进行类型转换,也不要为推断值添加类型注解。

CRITICAL:TanStack Router 以客户端为先。Loader 默认在客户端运行,而非服务器端。

CRITICAL:大多数组合式函数会返回 Ref<T>——在脚本中通过 .value 访问,在模板中则会自动解包。这是它与 React 版本最主要的区别。

CRITICAL:不要将 @tanstack/vue-router 与 vue-router(Vue 官方路由器)混淆。它们是完全不同的库,API 也各不相同。

完整设置:使用 Vite 进行基于文件的路由

1. 安装依赖
npm install @tanstack/vue-router
npm install -D @tanstack/router-plugin @vitejs/plugin-vue-jsx
2. 配置 Vite 插件
// 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: [
    // MUST come before vue()
    tanstackRouter({
      target: 'vue',
      autoCodeSplitting: true,
    }),
    vue(),
    vueJsx(), // Required for JSX/TSX route files
  ],
})
3. 创建根路由
// src/routes/__root.tsx
import { createRootRoute, Link, Outlet } from '@tanstack/vue-router'

export const Route = createRootRoute({
  component: RootLayout,
})

function RootLayout() {
  return (
    <>
      <nav>
        <Link to="/" activeProps={{ class: 'font-bold' }}>
          Home
        </Link>
        <Link to="/about" activeProps={{ class: 'font-bold' }}>
          About
        </Link>
      </nav>
      <hr />
      <Outlet />
    </>
  )
}
4. 创建路由文件
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/vue-router'

export const Route = createFileRoute('/')({
  component: HomePage,
})

function HomePage() {
  return <h1>Welcome Home</h1>
}
5. 创建 Router 实例并注册类型
// src/main.tsx
import { createApp } from 'vue'
import { RouterProvider, createRouter } from '@tanstack/vue-router'
import { routeTree } from './routeTree.gen'

const router = createRouter({ routeTree })

// REQUIRED — without this, Link/useNavigate/useSearch have no type safety
declare module '@tanstack/vue-router' {
  interface Register {
    router: typeof router
  }
}

const app = createApp(RouterProvider, { router })
app.mount('#root')

组合式函数参考

所有组合式函数均从 @tanstack/vue-router 导入。大多数会返回 Ref<T>——在脚本中通过 .value 访问,或在模板中自动解包。

useRouter() — 返回 TRouter(不是 Ref)
import { useRouter } from '@tanstack/vue-router'

const router = useRouter()
router.invalidate()
useRouterState() — 返回 Ref<T>

订阅路由器状态变化。它会暴露完整状态,因此会带来性能开销。需要获取匹配项或位置信息时,优先使用 useMatches 和 useLocation。

import { useRouterState } from '@tanstack/vue-router'

const isLoading = useRouterState({ select: (s) => s.isLoading })
// Access: isLoading.value
useNavigate() — 返回一个函数(不是 Ref)
import { useNavigate } from '@tanstack/vue-router'

const navigate = useNavigate()

async function handleSubmit() {
  await saveData()
  navigate({ to: '/posts/$postId', params: { postId: '123' } })
}
useSearch({ from }) — 返回 Ref<T>
import { useSearch } from '@tanstack/vue-router'

const search = useSearch({ from: '/products' })
// Access: search.value.page
useParams({ from }) — 返回 Ref<T>
import { useParams } from '@tanstack/vue-router'

const params = useParams({ from: '/posts/$postId' })
// Access: params.value.postId
useLoaderData({ from }) — 返回 Ref<T>
import { useLoaderData } from '@tanstack/vue-router'

const data = useLoaderData({ from: '/posts/$postId' })
// Access: data.value.post.content
useMatch({ from }) — 返回 Ref<T>
import { useMatch } from '@tanstack/vue-router'

const match = useMatch({ from: '/posts/$postId' })
// Access: match.value.loaderData.post.title
其他组合式函数
  • useMatches() — Ref<Array<Match>>,所有活跃的路由匹配项
  • useRouteContext({ from }) — Ref<T>,来自 beforeLoad 的上下文
  • useBlocker({ shouldBlockFn }) — 有未保存的更改时阻止导航
  • useCanGoBack() — Ref<boolean>
  • useLocation() — Ref<ParsedLocation>
  • useLoaderDeps({ from }) — Ref<T>,Loader 依赖值
  • useLinkProps() — 返回 LinkHTMLAttributes
  • useMatchRoute() — 返回一个函数;调用该函数会返回 Ref<false | Params>

组件参考

RouterProvider
import { RouterProvider } from '@tanstack/vue-router'
// In createApp or template
<RouterProvider :router="router" />
Link

类型安全的导航链接,带有用于显示激活状态的作用域插槽:

<Link to="/posts/$postId" :params="{ postId: '42' }">
  View Post
</Link>


<Link to="/about">
  <template #default="{ isActive }">
    <span :class="{ active: isActive }">About</span>
  </template>
</Link>
Outlet

渲染匹配到的子路由组件。

Navigate

声明式重定向(在 onMounted 中触发导航)。

Await

用于延迟数据的异步设置组件——与 Vue 的 <Suspense> 搭配使用。

CatchBoundary

使用 Vue 的 onErrorCaptured 实现错误边界。

Html 和 Body

Vue 专属的 SSR 外壳组件:

function RootComponent() {
  return (
    <Html>
      <head>
        <HeadContent />
      </head>
      <Body>
        <Outlet />
        <Scripts />
      </Body>
    </Html>
  )
}
ClientOnly

仅在 onMounted 后(即水合完成后)渲染子组件:

<ClientOnly fallback={<div>Loading...</div>}>
  <BrowserOnlyWidget />
</ClientOnly>

Vue 专属模式

使用 createLink 创建自定义链接组件
import { createLink } from '@tanstack/vue-router'
import { defineComponent, h } from 'vue'

const StyledLinkComponent = defineComponent({
  setup(props, { slots, attrs }) {
    return () => h('a', { ...attrs, class: 'styled-link' }, slots.default?.())
  },
})

const StyledLink = createLink(StyledLinkComponent)
渲染函数(h())

@tanstack/vue-router 中的所有组件内部都使用 h() 渲染函数。路由组件既可以使用 SFC 模板,也可以使用渲染函数:

MyRoute.component.vue 中的 SFC 模板(用户代码中最常用):

<template>
  <div>{{ data.title }}</div>
</template>

<script setup>
import { useLoaderData } from '@tanstack/vue-router'
const data = useLoaderData({ from: '/posts/$postId' })
</script>
使用 Router 上下文进行身份验证
import { createRootRouteWithContext } from '@tanstack/vue-router'

const rootRoute = createRootRouteWithContext<{ auth: AuthState }>()({
  component: RootComponent,
})

const router = createRouter({
  routeTree,
  context: { auth: authState },
})

// In a route — access via beforeLoad
beforeLoad: ({ context }) => {
  if (!context.auth.isAuthenticated) {
    throw redirect({ to: '/login' })
  }
}
用于代码拆分的 Vue 文件约定

使用 autoCodeSplitting 时,Vue 路由可以选择采用拆分文件约定。这并非必需——单文件 .tsx 路由也可以正常工作。拆分文件适合将路由配置与组件分开:

  • myRoute.ts — 路由配置(搜索参数、loader、beforeLoad)
  • myRoute.component.vue — 路由组件(延迟加载)
  • myRoute.errorComponent.vue — 错误组件(延迟加载)
  • myRoute.notFoundComponent.vue — 未找到页面的组件(延迟加载)
  • myRoute.lazy.ts — 延迟加载的路由选项

常见错误

1. 严重:忘记在 script 块中使用 .value

组合式函数会返回 Ref<T>——在 <script> 中通过 .value 访问。在模板中会自动解包。

// WRONG — accessing Ref without .value in script
const params = useParams({ from: '/posts/$postId' })
console.log(params.postId) // undefined!

// CORRECT — use .value
const params = useParams({ from: '/posts/$postId' })
console.log(params.value.postId)
2. 高:与 vue-router(官方版)混淆

@tanstack/vue-router 不是 vue-router。不要使用 vue-router 中的 <router-view>、<router-link>、useRoute()、useRouter()。

// WRONG — official vue-router imports
import { useRoute, useRouter } from 'vue-router'

// CORRECT — TanStack Vue Router imports
import { useMatch, useRouter } from '@tanstack/vue-router'
3. 高:在 beforeLoad 或 loader 中使用 Vue 钩子

beforeLoad 和 loader 不是组件的 setup 函数,而是普通的异步函数。不能在其中使用 Vue 组合式函数。请改用路由器上下文传递状态。

4. 中:插件目标设置错误

必须在路由器插件配置中设置 target: 'vue'。默认值为 'react'。

交叉引用

  • @tanstack/router-core#router-core — 所有针对特定领域模式的子技能(搜索参数、数据加载、导航、身份验证、SSR 等)

评论

支持 Markdown