自定义主题
VitePress 主题只是一个对象,包含四个属性:Layout
、NotFound
、enhanceApp
和 setup
。
interface Theme {
Layout: Component // Vue 3 组件
NotFound?: Component
enhanceApp?: (ctx: EnhanceAppContext) => Awaitable<void>
setup?: () => void
}
interface EnhanceAppContext {
app: App // Vue 3 应用实例
router: Router // VitePress 路由器实例
siteData: Ref<SiteData> // 站点级元数据
}
最小主题
要创建自定义主题,首先在 .vitepress/theme/index.js
或 .vitepress/theme/index.ts
中创建一个主题入口文件:
// .vitepress/theme/index.js
import Layout from './Layout.vue'
export default {
Layout,
NotFound: () => 'custom 404', // <- 这是一个 Vue 3 函数组件
enhanceApp({ app, router, siteData }) {
// app 是 Vue 3 应用实例,来自 createApp()
// router 是 VitePress 路由器实例
// siteData 是当前站点级元数据的 ref
},
setup() {
// 这个函数将在每个 VitePress 页面的 Vue 应用中执行,
// 包括 md 页面和 .vue 页面
}
}
提示
如果你使用 TypeScript,记得将 index.js
重命名为 index.ts
。
Layout
组件是每个页面的根组件。最小的主题需要包含一个 Layout
组件,该组件处理页面内容的渲染:
<!-- .vitepress/theme/Layout.vue -->
<template>
<h1>Custom Layout!</h1>
<!-- 这是 markdown 内容将被渲染的地方 -->
<Content />
</template>
<Content />
组件显示渲染的 markdown 内容。就是这样 - 现在你有了一个自定义主题!
你也可以提供一个 NotFound
组件:
<!-- .vitepress/theme/NotFound.vue -->
<template>
<h1>Page not found</h1>
</template>
// .vitepress/theme/index.js
import Layout from './Layout.vue'
import NotFound from './NotFound.vue'
export default {
Layout,
NotFound
}
包含默认主题
如果你想扩展和自定义默认主题,你可以从 vitepress/theme
导入它并在自定义主题中扩展它。以下是一些常见的自定义示例:
注册全局组件
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// 注册自定义全局组件
app.component('MyGlobalComponent', /* ... */)
}
}
由于我们使用 Vite,你也可以利用 Vite 的 glob 导入功能 来自动注册组件目录。
自定义 CSS
默认主题 CSS 可以通过覆盖根级 CSS 变量来自定义:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme
/* .vitepress/theme/custom.css */
:root {
--vp-c-brand-1: #646cff;
--vp-c-brand-2: #747bff;
}
查看默认主题 CSS 变量,这些变量可以被覆盖。
布局插槽
默认主题的 <Layout />
组件有一些插槽,可以用来在页面的某些位置注入内容。这里是一个在大纲前注入组件的例子:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
extends: DefaultTheme,
// 覆盖 Layout 以提供插槽
Layout: MyLayout
}
<!--.vitepress/theme/MyLayout.vue-->
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout>
<template #aside-outline-before>
My custom sidebar top content
</template>
</Layout>
</template>
或者你可以使用渲染函数。
// .vitepress/theme/index.js
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import MyComponent from './MyComponent.vue'
export default {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
// https://cn.vuejs.org/guide/extras/render-function.html#named-slots
'aside-outline-before': () => h(MyComponent)
})
}
}
默认主题布局的完整插槽列表:
- 当
layout: 'doc'
(默认)在 frontmatter 中激活时:doc-top
doc-bottom
doc-footer-before
doc-before
doc-after
sidebar-nav-before
sidebar-nav-after
aside-top
aside-bottom
aside-outline-before
aside-outline-after
aside-ads-before
aside-ads-after
- 当
layout: 'home'
在 frontmatter 中激活时:home-hero-before
home-hero-info-before
home-hero-info
home-hero-info-after
home-hero-actions-after
home-hero-image
home-hero-after
home-features-before
home-features-after
- 当
layout: 'page'
在 frontmatter 中激活时:page-top
page-bottom
- 在 not found (404) 页面上:
not-found
- 总是:
layout-top
layout-bottom
nav-bar-title-before
nav-bar-title-after
nav-bar-content-before
nav-bar-content-after
nav-screen-content-before
nav-screen-content-after
使用 View Transitions API
在外观切换时
你可以扩展默认主题以在颜色模式之间切换时提供自定义过渡。例如:
/* .vitepress/theme/custom.css */
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-old(root),
.dark::view-transition-new(root) {
z-index: 1;
}
::view-transition-new(root),
.dark::view-transition-old(root) {
z-index: 9999;
}
.VPSwitchAppearance {
view-transition-name: vp-switch-appearance;
}
在路由更改时
VitePress 对路由更改提供内置支持。将以下内容添加到你的自定义 CSS:
/* .vitepress/theme/custom.css */
@media (prefers-reduced-motion: no-preference) {
.VPContent {
view-transition-name: vp-content;
}
.VPContent.is-appearing,
.VPContent.is-disappearing {
view-transition-name: vp-content;
}
}
警告
View Transitions API 仍然是实验性的。它目前仅在基于 Chromium 的浏览器中工作,但预计将来会得到更广泛的支持。请谨慎使用。
分发自定义主题
分发自定义主题的最简单方法是将其作为模板存储库在 GitHub 上提供。
如果你想将主题作为 npm 包分发,请遵循以下步骤:
- 在 package.json 中将主题对象导出为默认导出。
- 如果适用,将主题配置类型定义导出为
ThemeConfig
。 - 如果你的主题需要调整 VitePress 配置,也要在包下的子路径中导出该配置(例如
my-theme/config
),以便用户可以扩展它。 - 记录主题配置选项(通过配置文件和 frontmatter)。
- 提供清晰的说明,说明如何安装和使用你的主题(见下文)。
主题配置
用户将能够通过 .vitepress/config.js
文件中的 themeConfig
选项配置你的主题。
// .vitepress/config.js
export default {
themeConfig: {
// Type is `ThemeConfig`
}
}
主题安装
假设你将主题发布为 my-theme
,用户将通过以下方式安装它:
npm add my-theme
然后在他们的主题入口中导入和使用它:
// .vitepress/theme/index.js
import MyTheme from 'my-theme'
export default MyTheme
就像扩展默认主题一样,用户也可以扩展你的主题:
// .vitepress/theme/index.js
import MyTheme from 'my-theme'
export default {
extends: MyTheme,
enhanceApp({ app }) {
// ...
}
}