运行时 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 | falseVitePressData 逐字段详解
| 字段 | 类型 | 说明 |
|---|---|---|
site | Ref<SiteData> | 站点级数据,包含 title、description、base、lang、themeConfig 等 |
page | Ref<PageData> | 当前页面数据,包含 title、relativePath、headers、frontmatter 等 |
frontmatter | Ref<Record<string, any>> | 当前页面 frontmatter 的便捷访问 |
themeConfig | Ref<any> | 主题配置对象的便捷访问 |
lang | Ref<string> | 当前页面语言,如 'zh-CN'、'en-US' |
title | Ref<string> | 当前页面标题 |
description | Ref<string> | 当前页面描述 |
isDark | Ref<boolean> | 当前是否为深色模式,可写,修改可切换深色模式 |
theme | Ref<any> | 主题入口导出的数据(通过 Layout 的 setup 返回) |
SiteData 字段详解
| 字段 | 类型 | 说明 |
|---|---|---|
title | string | 站点标题 |
description | string | 站点描述 |
base | string | 站点基础路径 |
lang | string | 站点语言 |
head | HeadConfig[] | 全局 head 标签 |
themeConfig | any | 主题配置对象 |
PageData 字段详解
| 字段 | 类型 | 说明 |
|---|---|---|
title | string | 页面标题(来自 frontmatter 或文件名) |
titleTemplate | string | boolean | 标题模板 |
description | string | 页面描述 |
relativePath | string | 相对于源目录的路径,如 'guide/installation.md' |
filePath | string | 文件系统绝对路径 |
headers | Header[] | 页面标题列表 |
frontmatter | Record<string, any> | 页面 frontmatter |
params | Record<string, string> | 动态路由参数 |
isNotFound | boolean | 是否为 404 页面 |
lastUpdated | number | 最后更新时间戳 |
Header 类型
| 字段 | 类型 | 说明 |
|---|---|---|
level | number | 标题级别(1-6) |
title | string | 标题文本 |
slug | string | URL 锚点,如 'installation' |
link | string | 完整链接,如 '#installation' |
children | Header[] | 子标题列表 |
使用示例
读取页面元数据
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 字段详解
| 字段 | 类型 | 说明 |
|---|---|---|
path | string | 当前页面路径,如 /guide/installation.html |
data | PageData | 当前页面数据(同 useData().page) |
component | Component | 当前页面 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 | 刷新当前页面 |
route | Route | 当前路由对象 |
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 API | defineClientComponent(() => import('./Chart.vue')) |
| 地图组件(Mapbox、Leaflet) | 依赖 window 对象 | defineClientComponent(() => import('./Map.vue')) |
| 代码编辑器(Monaco) | 依赖 Web Workers | defineClientComponent(() => import('./Editor.vue')) |
| 评论系统(Giscus) | 需要客户端 API | defineClientComponent(() => 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 详解
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
includeSrc | boolean | false | 包含原始 Markdown 源码 |
excerpt | boolean | ((file: MarkdownFile) => string) | false | 生成摘要,true 使用默认 <!-- more --> 分隔 |
includeUnresolved | boolean | false | 包含未解析的文件 |
transform | (data: MarkdownFile[]) => T[] | — | 数据转换函数 |
rawData 中每个条目的结构
| 字段 | 类型 | 说明 |
|---|---|---|
url | string | 页面 URL |
src | string | 原始 Markdown 源码(需 includeSrc: true) |
html | string | 渲染后的 HTML |
excerpt | string | 摘要(需 excerpt: true) |
frontmatter | Record<string, any> | 页面 frontmatter |
filePath | string | 文件路径 |
在组件中使用
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 选项详解
| 选项 | 类型 | 说明 |
|---|---|---|
watch | string[] | 监听文件变化的 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>参考链接
- 站点配置 - 完整配置选项参考
- Frontmatter 配置 - 页面级配置
- 主题配置 - 主题配置选项
- 主题 API - 主题开发 API
- 构建 API - 构建 API 参考