Skip to content

VitePress 与 Vue Router

深入理解 VitePress 的路由机制,掌握文件路由、动态路由和编程式导航。

路由架构

VitePress 路由 vs Vue Router

VitePress 基于文件系统自动生成路由,底层使用的是简化版的 Vue Router:

特性VitePress 路由标准 Vue Router
路由定义基于文件系统自动生成手动配置
动态路由通过 paths 文件通过 :param 语法
嵌套路由目录层级自动映射手动配置 children
路由守卫onBeforeRouteChangebeforeEach
懒加载自动按页面分割手动 import()

文件到路由的映射规则

docs/
├── index.md              → /
├── about.md              → /about
├── guide/
│   ├── index.md          → /guide/
│   ├── installation.md   → /guide/installation
│   └── advanced/
│       └── ssr.md        → /guide/advanced/ssr
└── api/
    ├── index.md          → /api/
    └── [id].md           → /api/:id(动态路由)

useRoute 和 useRouter

useRoute — 获取当前路由信息

vue
<script setup lang="ts">
import { useRoute } from 'vitepress'

const route = useRoute()
</script>

<template>
  <div>
    <p>当前路径: {{ route.path }}</p>
    <p>页面数据: {{ route.data.title }}</p>
    <p>Frontmatter: {{ route.data.frontmatter }}</p>
  </div>
</template>

useRouter — 编程式导航

vue
<script setup lang="ts">
import { useRouter } from 'vitepress'

const router = useRouter()

function navigateToGuide() {
  // 导航到指定路径
  router.go('/guide/')
}

function goBack() {
  // 后退一步
  router.go(-1)
}

function goForward() {
  // 前进一步
  router.go(1)
}
</script>

路由钩子

可用的路由钩子

typescript
// .vitepress/theme/index.ts
import { useRouter } from 'vitepress'

export default {
  setup() {
    const router = useRouter()

    // 页面跳转前
    router.onBeforeRouteChange = (to) => {
      console.log('即将导航到:', to)
      // 返回 false 可取消导航
    }

    // 页面加载前
    router.onBeforePageLoad = (to) => {
      console.log('开始加载页面:', to)
    }

    // 页面加载后
    router.onAfterPageLoad = (to) => {
      console.log('页面加载完成:', to)
    }

    // 路由切换后(DOM 已更新)
    router.onAfterRouteChanged = (to) => {
      console.log('路由已切换到:', to)
      // 可用于页面分析、滚动重置等
    }
  }
}

实际应用场景

页面访问统计

typescript
router.onAfterRouteChanged = (to) => {
  // 上报页面访问
  if (typeof window !== 'undefined' && window.gtag) {
    window.gtag('config', 'GA-ID', {
      page_path: to
    })
  }
}

页面切换进度条

typescript
import { ref } from 'vue'

const isLoading = ref(false)

router.onBeforeRouteChange = () => {
  isLoading.value = true
}

router.onAfterRouteChanged = () => {
  isLoading.value = false
}

路由切换时滚动到顶部

typescript
router.onAfterRouteChanged = () => {
  window.scrollTo(0, 0)
}

动态路由

创建动态路由页面

使用 [param].md 语法创建动态路由:

docs/
├── blog/
│   ├── [id].md           # /blog/:id
│   └── [id].paths.ts     # 动态路由数据

定义路径数据

typescript
// docs/blog/[id].paths.ts
import { createContentLoader } from 'vitepress'

export default {
  async paths() {
    const posts = await createContentLoader('blog/*.md')
    return posts.map(post => ({
      params: {
        id: post.url.replace('/blog/', '').replace('.html', '')
      },
      content: post.excerpt
    }))
  }
}

在页面中使用参数

markdown
---
title: 博客文章
---

<!-- docs/blog/[id].md -->

<script setup lang="ts">
import { useData } from 'vitepress'

const { page } = useData()
const postId = page.value.params.id
</script>

<template>
  <article>
    <h1>文章 ID: {{ postId }}</h1>
  </article>
</template>

路由重写

配置路由重写

typescript
// .vitepress/config.mts
export default defineConfig({
  rewrites: {
    // 将 zh/guide/ 重写为 guide/
    'zh/guide/:id': 'guide/:id',
    'en/guide/:id': 'en/guide/:id'
  }
})

重写与 i18n

路由重写常用于国际化场景,将不同语言的源文件映射到不同的路由:

源文件结构:
docs/
├── zh/
│   └── guide/
│       └── index.md
├── en/
│   └── guide/
│       └── index.md

重写后的路由:
/zh/guide/ → /guide/      (中文)
/en/guide/ → /en/guide/   (英文)

404 路由

自定义 404 页面

创建 docs/404.md

markdown
---
layout: page
---

<div class="not-found">
  <h1>404</h1>
  <p>页面未找到</p>
  <a href="/">返回首页</a>
</div>

<style scoped>
.not-found {
  text-align: center;
  padding: 96px 24px;
}
</style>

在主题中自定义 404

typescript
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import NotFound from './NotFound.vue'

export default {
  extends: DefaultTheme,
  NotFound  // 自定义 404 组件
}

SPA 路由 vs MPA 路由

SPA 路由(默认)

  • 页面切换由 Vue Router 处理
  • 不刷新浏览器
  • 支持 Vue 过渡动画
  • JS bundle 较大

MPA 路由

typescript
export default defineConfig({
  mpa: true
})
  • 每次导航都是完整的页面请求
  • 无 JavaScript 依赖
  • 更快的首屏加载
  • 不支持客户端路由钩子

常见问题

路由不生效

检查文件路径是否正确,注意:

  • 文件名使用小写字母和连字符
  • index.md 对应目录根路径
  • 不需要 .md.html 后缀

动态路由页面为空

确保 [param].paths.ts 文件正确导出了 paths 函数,且返回的 params 与文件名中的参数名匹配。

路由钩子在 MPA 模式下不生效

MPA 模式没有客户端路由,路由钩子不可用。使用 SPA 模式或改用服务端方案。

相关链接

贡献者

加载中...

想要成为贡献者?

在 CNB 上参与贡献