Skip to content

运行时 API

VitePress 提供了一组运行时 API,用于在 Vue 组件和 Markdown 中访问站点数据、路由信息和进行编程式导航。

快速导航: useData | useRoute | useRouter | withBase | usePrefetch | defineClientComponent | Content | createContentLoader | 数据加载


useData

获取站点和页面的所有数据,是最常用的运行时 API。

类型签名

ts
function useData(): VitePressData

基础用法

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

const {
  site,          // 站点级数据(Ref<SiteData>)
  page,          // 当前页面数据(Ref<PageData>)
  frontmatter,   // 当前页面 frontmatter(Ref<Record<string, any>>)
  themeConfig,   // 主题配置(Ref<any>)
  lang,          // 当前语言(Ref<string>)
  title,         // 页面标题(Ref<string>)
  description,   // 页面描述(Ref<string>)
  isDark         // 是否深色模式(Ref<boolean>)
} = useData()
</script>

注意响应式

useData() 返回的所有属性都是 Ref,在模板中会自动解包,但在 <script> 中需要使用 .value

ts
const { isDark } = useData()

// ❌ 错误:isDark 是 Ref
console.log(isDark) // Ref<boolean>

// ✅ 正确:使用 .value
console.log(isDark.value) // true | false

VitePressData 逐字段详解

字段类型说明
siteRef<SiteData>站点级数据,包含 titledescriptionbaselangthemeConfig
pageRef<PageData>当前页面数据,包含 titlerelativePathheadersfrontmatter
frontmatterRef<Record<string, any>>当前页面 frontmatter 的便捷访问
themeConfigRef<any>主题配置对象的便捷访问
langRef<string>当前页面语言,如 'zh-CN''en-US'
titleRef<string>当前页面标题
descriptionRef<string>当前页面描述
isDarkRef<boolean>当前是否为深色模式,可写,修改可切换深色模式
themeRef<any>主题入口导出的数据(通过 Layoutsetup 返回)

SiteData 字段详解

字段类型说明
titlestring站点标题
descriptionstring站点描述
basestring站点基础路径
langstring站点语言
headHeadConfig[]全局 head 标签
themeConfigany主题配置对象

PageData 字段详解

字段类型说明
titlestring页面标题(来自 frontmatter 或文件名)
titleTemplatestring | boolean标题模板
descriptionstring页面描述
relativePathstring相对于源目录的路径,如 'guide/installation.md'
filePathstring文件系统绝对路径
headersHeader[]页面标题列表
frontmatterRecord<string, any>页面 frontmatter
paramsRecord<string, string>动态路由参数
isNotFoundboolean是否为 404 页面
lastUpdatednumber最后更新时间戳

Header 类型

字段类型说明
levelnumber标题级别(1-6)
titlestring标题文本
slugstringURL 锚点,如 'installation'
linkstring完整链接,如 '#installation'
childrenHeader[]子标题列表

使用示例

读取页面元数据

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

const { page, frontmatter } = useData()
</script>

<template>
  <article>
    <h1>{{ frontmatter.title }}</h1>
    <p>路径: {{ page.relativePath }}</p>
    <p v-if="frontmatter.date">发布于: {{ frontmatter.date }}</p>
  </article>
</template>

条件渲染

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

const { frontmatter } = useData()
</script>

<template>
  <div v-if="frontmatter.layout === 'home'">
    <!-- 首页专属内容 -->
  </div>
  <div v-else>
    <!-- 文档页面内容 -->
  </div>
</template>

深色模式切换

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

const { isDark } = useData()

function toggleDark() {
  isDark.value = !isDark.value
}
</script>

<template>
  <button @click="toggleDark" :aria-label="isDark ? '切换浅色' : '切换深色'">
    {{ isDark ? '🌙' : '☀️' }}
  </button>
</template>

useRoute

获取当前路由信息,返回当前页面的路由对象。

类型签名

ts
function useRoute(): Route

基础用法

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

const route = useRoute()
</script>

<template>
  <p>当前路径: {{ route.path }}</p>
</template>

Route 字段详解

字段类型说明
pathstring当前页面路径,如 /guide/installation.html
dataPageData当前页面数据(同 useData().page
componentComponent当前页面 Vue 组件

useRoute vs useData

  • useRoute() 侧重路由信息(路径、导航)
  • useData() 侧重数据信息(frontmatter、站点配置)
  • 两者有部分重叠,根据场景选择

监听路由变化

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

const route = useRoute()

watch(
  () => route.path,
  (newPath) => {
    console.log('页面切换到:', newPath)
  }
)
</script>

useRouter

访问路由器实例,进行编程式导航和注册导航钩子。

类型签名

ts
function useRouter(): Router

基础用法

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

const router = useRouter()

function goHome() {
  router.go('/')  // 导航到首页
}

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

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

Router 方法详解

方法/属性类型说明
go(path)(path: string | number) => void导航到指定路径,或传入数字进行前进/后退
reload()() => void刷新当前页面
routeRoute当前路由对象
onBeforeRouteChange(to: string) => boolean | void路由变化前钩子,返回 false 取消导航
onBeforePageLoad(to: string) => void页面加载前钩子
onAfterPageLoad(to: string) => void页面加载后钩子
onAfterRouteChanged(to: string) => void路由变化后钩子

导航钩子示例

页面切换时记录日志

ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import { useRouter } from 'vitepress'
import { onMounted } from 'vue'

export default {
  ...DefaultTheme,
  setup() {
    const router = useRouter()

    onMounted(() => {
      router.onBeforeRouteChange = (to) => {
        console.log('即将导航到:', to)
      }

      router.onAfterRouteChanged = (to) => {
        console.log('已导航到:', to)
        // 发送页面浏览统计
        if (typeof window !== 'undefined' && window.gtag) {
          window.gtag('config', 'GA_ID', { page_path: to })
        }
      }
    })
  }
}

条件性阻止导航

ts
router.onBeforeRouteChange = (to) => {
  // 检查是否有未保存的表单数据
  if (hasUnsavedChanges.value) {
    return confirm('有未保存的更改,确定要离开吗?')
  }
}

页面加载进度条

ts
import NProgress from 'nprogress'

router.onBeforeRouteChange = () => {
  NProgress.start()
}

router.onAfterRouteChanged = () => {
  NProgress.done()
}

withBase

为 URL 添加 base 前缀,确保在子路径部署时路径正确。

类型签名

ts
function withBase(path: string): string

基础用法

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

<template>
  <img :src="withBase('/images/logo.png')" alt="Logo" />
  <a :href="withBase('/guide/')">指南</a>
</template>

工作原理

base 配置withBase('/images/logo.png') 结果
'/'/images/logo.png
'/docs/'/docs/images/logo.png
'/my-site/'/my-site/images/logo.png

何时使用 withBase

  • 在 Vue 组件中动态拼接路径时使用
  • Markdown 中的链接和图片 VitePress 会自动处理,无需手动调用
  • public 目录下的静态资源引用建议使用

usePrefetch

预加载页面资源,优化用户体验。

类型签名

ts
function usePrefetch(): { prefetch: (path: string) => void }

基础用法

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

const { prefetch } = usePrefetch()
</script>

<template>
  <a
    href="/guide/getting-started"
    @mouseover="prefetch('/guide/getting-started')"
  >
    快速开始
  </a>
</template>

自动预加载

VitePress 默认会在用户 hover 导航链接时自动预加载目标页面,通常无需手动使用 usePrefetch。仅在自定义组件中需要预加载时使用。

defineClientComponent

定义仅在客户端渲染的组件,解决 SSG 构建时第三方库不可用的问题。

类型签名

ts
function defineClientComponent(
  loader: () => Promise<Component>,
  props?: Record<string, any>,
  slots?: Record<string, () => VNode[]>
): Component

基础用法

vue
<script setup lang="ts">
import { defineClientComponent } from 'vitepress/client'

// 异步加载仅在客户端使用的组件
const Chart = defineClientComponent(() => {
  return import('./Chart.vue')
})
</script>

<template>
  <ClientOnly>
    <Chart />
  </ClientOnly>
</template>

带 Props 和 Slots

vue
<script setup lang="ts">
import { defineClientComponent } from 'vitepress/client'

const Map = defineClientComponent(
  () => import('./Map.vue'),
  // 传递给组件的 props
  { center: [0, 0], zoom: 10 },
  // slots(可选)
  {
    default: () => h('p', '加载中...')
  }
)
</script>

<template>
  <Map />
</template>

常见使用场景

场景原因示例
图表库(ECharts、Chart.js)依赖 DOM APIdefineClientComponent(() => import('./Chart.vue'))
地图组件(Mapbox、Leaflet)依赖 window 对象defineClientComponent(() => import('./Map.vue'))
代码编辑器(Monaco)依赖 Web WorkersdefineClientComponent(() => import('./Editor.vue'))
评论系统(Giscus)需要客户端 APIdefineClientComponent(() => import('./Comments.vue'))

Content 组件

渲染当前页面的 Markdown 内容,常用于自定义布局。

基础用法

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

<template>
  <div class="custom-layout">
    <nav>...</nav>
    <main>
      <Content />
    </main>
  </div>
</template>

自定义布局中使用

vue
<!-- .vitepress/theme/layouts/CustomLayout.vue -->
<script setup lang="ts">
import { Content } from 'vitepress'
import { useData } from 'vitepress'

const { frontmatter } = useData()
</script>

<template>
  <div class="custom-layout">
    <header v-if="frontmatter.title">
      <h1>{{ frontmatter.title }}</h1>
    </header>
    <Content />
    <footer>...</footer>
  </div>
</template>

createContentLoader

批量加载和处理 Markdown 文件,常用于生成文章列表、标签页、站点地图等。

类型签名

ts
function createContentLoader(
  pattern: string | string[],
  options?: ContentLoaderOptions
): ContentLoader

基础用法

ts
// .vitepress/data/posts.data.ts
import { createContentLoader } from 'vitepress'

export default createContentLoader('blog/*.md', {
  includeSrc: true,
  excerpt: true,
  transform(rawData) {
    return rawData
      .sort((a, b) =>
        +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)
      )
      .map(({ url, frontmatter, excerpt }) => ({
        url,
        title: frontmatter.title,
        date: frontmatter.date,
        excerpt,
        tags: frontmatter.tags || []
      }))
  }
})

ContentLoaderOptions 详解

选项类型默认值说明
includeSrcbooleanfalse包含原始 Markdown 源码
excerptboolean | ((file: MarkdownFile) => string)false生成摘要,true 使用默认 <!-- more --> 分隔
includeUnresolvedbooleanfalse包含未解析的文件
transform(data: MarkdownFile[]) => T[]数据转换函数

rawData 中每个条目的结构

字段类型说明
urlstring页面 URL
srcstring原始 Markdown 源码(需 includeSrc: true
htmlstring渲染后的 HTML
excerptstring摘要(需 excerpt: true
frontmatterRecord<string, any>页面 frontmatter
filePathstring文件路径

在组件中使用

vue
<script setup lang="ts">
import { data as posts } from '../data/posts.data'
</script>

<template>
  <ul class="post-list">
    <li v-for="post in posts" :key="post.url">
      <a :href="post.url">{{ post.title }}</a>
      <span>{{ post.date }}</span>
      <p v-if="post.excerpt" v-html="post.excerpt" />
    </li>
  </ul>
</template>

多数据加载器

ts
// .vitepress/data/posts.data.ts
import { createContentLoader } from 'vitepress'

export default createContentLoader('blog/*.md', {
  excerpt: true,
  transform(data) {
    return data.map(({ url, frontmatter }) => ({
      url,
      title: frontmatter.title,
      date: frontmatter.date
    }))
  }
})
ts
// .vitepress/data/wiki.data.ts
import { createContentLoader } from 'vitepress'

export default createContentLoader('wiki/*.md', {
  transform(data) {
    return data.map(({ url, frontmatter }) => ({
      url,
      title: frontmatter.title,
      category: frontmatter.category
    }))
  }
})

数据加载器

VitePress 支持在构建时加载外部数据并在组件中使用。

defineLoader

定义自定义数据加载器,用于从 API 或文件系统加载数据。

ts
// .vitepress/data/github.data.ts
import { defineLoader } from 'vitepress'

export default defineLoader({
  // 监听文件变化触发重新加载
  watch: ['blog/*.md'],

  // 加载函数
  async load() {
    const response = await fetch('https://api.github.com/repos/vuejs/vitepress')
    const data = await response.json()
    return {
      stars: data.stargazers_count,
      forks: data.forks_count,
      issues: data.open_issues_count
    }
  }
})

使用数据加载器

vue
<script setup lang="ts">
// 导入 data 加载器导出的数据
import { data } from '../data/github.data'
</script>

<template>
  <div class="github-stats">
    <span>⭐ {{ data.stars }}</span>
    <span>🍴 {{ data.forks }}</span>
    <span>🐛 {{ data.issues }}</span>
  </div>
</template>

defineLoader 选项详解

选项类型说明
watchstring[]监听文件变化的 glob 模式
load() => Promise<T>数据加载函数

使用示例

面包屑组件

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

const route = useRoute()

const breadcrumbs = computed(() => {
  const parts = route.path.split('/').filter(Boolean)
  return parts.map((part, index) => ({
    text: part.charAt(0).toUpperCase() + part.slice(1),
    link: '/' + parts.slice(0, index + 1).join('/') + '/'
  }))
})
</script>

<template>
  <nav class="breadcrumb" aria-label="面包屑导航">
    <a href="/">首页</a>
    <span v-for="item in breadcrumbs" :key="item.link">
      / <a :href="item.link">{{ item.text }}</a>
    </span>
  </nav>
</template>

页面标题组件

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

const { page, frontmatter } = useData()
</script>

<template>
  <header class="page-header">
    <h1>{{ frontmatter.title || page.title }}</h1>
    <p v-if="frontmatter.description">{{ frontmatter.description }}</p>
    <div v-if="frontmatter.date" class="meta">
      发布于 {{ frontmatter.date }}
    </div>
  </header>
</template>

文章列表组件

vue
<script setup lang="ts">
import { data as posts } from '../data/posts.data'
import { withBase } from 'vitepress'
</script>

<template>
  <div class="post-list">
    <article v-for="post in posts" :key="post.url" class="post-item">
      <h2>
        <a :href="withBase(post.url)">{{ post.title }}</a>
      </h2>
      <time v-if="post.date">{{ post.date }}</time>
      <div v-if="post.excerpt" v-html="post.excerpt" />
    </article>
  </div>
</template>

目录组件

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

const { page } = useData()
</script>

<template>
  <nav class="toc" v-if="page.headers.length">
    <h3>目录</h3>
    <ul>
      <li v-for="header in page.headers" :key="header.slug">
        <a :href="header.link">{{ header.title }}</a>
      </li>
    </ul>
  </nav>
</template>

参考链接

贡献者

加载中...

想要成为贡献者?

在 CNB 上参与贡献