扩展默认主题
VitePress 的默认主题针对文档进行了优化,并且可以自定义。请查阅默认主题配置概述以获取完整的选项列表。
但是,在许多情况下,仅仅配置是不够的。例如:
- 你需要调整 CSS 样式;
- 你需要修改 Vue 应用实例,例如注册全局组件;
- 你需要通过布局插槽将自定义内容注入到主题中。
这些高级自定义将需要使用自定义主题来"扩展"默认主题。
主题解析
你可以通过在 .vitepress/theme/index.js
或 .vitepress/theme/index.ts
("主题入口文件")中创建一个主题对象来启用自定义主题:
.
├─ docs # 项目根目录
│ ├─ .vitepress
│ │ ├─ theme
│ │ │ └─ index.js # 主题入口
│ │ └─ config.js # 配置文件
│ └─ index.md
└─ package.json
VitePress 主题只是一个包含四个属性的对象,定义如下:
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
// 你可以直接导入 Vue 文件
// VitePress 已预配置 @vitejs/plugin-vue
import Layout from './Layout.vue'
import NotFound from './NotFound.vue'
export default {
Layout,
NotFound,
enhanceApp({ app, router, siteData }) {
// ...
}
}
默认导出是自定义主题的唯一约定。在你的自定义主题中,它就像一个普通的 Vite + Vue 3 应用程序一样工作。请注意,主题还需要兼容 SSR。
要分发主题,只需在你的包入口中导出对象。要使用外部主题,请从自定义主题入口导入并重新导出它:
// .vitepress/theme/index.js
import Theme from 'awesome-vitepress-theme'
export default Theme
扩展默认主题
如果你想扩展和自定义默认主题,你可以从 vitepress/theme
导入它并在自定义主题中扩展它:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
export default DefaultTheme
现在让我们说你想添加一些自定义 CSS:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme
如果你想注册全局组件:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// 注册自定义全局组件
app.component('MyGlobalComponent', /* ... */)
}
}
如果你想修改默认主题:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
extends: DefaultTheme,
// 覆盖 Layout 以提供插槽
Layout: MyLayout
}
自定义 CSS
默认主题 CSS 可以通过覆盖根级 CSS 变量来自定义:
/* .vitepress/theme/custom.css */
:root {
--vp-c-brand-1: #646cff;
--vp-c-brand-2: #747bff;
}
查看默认主题 CSS 变量,这些变量可以被覆盖。
使用不同的字体
VitePress 默认使用 Inter 作为字体,并将在构建输出中包含字体。字体也会在生产中自动预加载。但是,如果你想使用不同的主字体,这可能不是理想的。
要避免在构建输出中包含 Inter,请从 vitepress/theme-without-fonts
导入主题:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme-without-fonts'
import './my-fonts.css'
export default DefaultTheme
/* .vitepress/theme/my-fonts.css */
/* 注册你的自定义字体系列并告诉 VitePress 使用它 */
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
:root {
--vp-font-family-base: 'Roboto', sans-serif;
}
警告
如果你使用可选组件,如团队页面组件,请确保也从 vitepress/theme-without-fonts
导入它们!
如果你的字体是通过 @font-face
引用的本地文件,它将作为资源处理并包含在 .vitepress/dist/assets/
下,并带有散列文件名。要预加载此文件,请使用 transformHead 构建钩子:
// .vitepress/config.js
export default {
transformHead({ assets }) {
// 相应地调整正则表达式以匹配你的字体
const myFontFile = assets.find(file => /font-name\.\w+\.woff2/)
if (myFontFile) {
return [
[
'link',
{
rel: 'preload',
href: myFontFile,
as: 'font',
type: 'font/woff2',
crossorigin: ''
}
]
]
}
}
}
布局插槽
默认主题的 <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
全局组件注册
enhanceApp
函数是注册全局 Vue 组件的地方:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('VueClickAwayExample', VueClickAwayExample)
}
}
如果你使用 TypeScript:
// .vitepress/theme/index.ts
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import VueClickAwayExample from './components/VueClickAwayExample.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('VueClickAwayExample', VueClickAwayExample)
}
} satisfies Theme
由于我们使用 Vite,你也可以利用 Vite 的 glob 导入功能 来自动注册组件目录。