扩展默认主题
VitePress 的默认主题针对文档进行了优化,可以进行自定义。请参阅 默认主题配置概述 以获取选项的完整列表。
但是,在某些情况下,仅靠配置是不够的。例如
- 您需要调整 CSS 样式;
- 您需要修改 Vue 应用程序实例,例如注册全局组件;
- 您需要通过布局插槽将自定义内容注入主题。
这些高级自定义需要使用“扩展”默认主题的自定义主题。
提示
在继续之前,请确保先阅读 使用自定义主题 以了解自定义主题的工作原理。
自定义 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 变量。
使用不同的字体
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/custom.css */
:root {
--vp-font-family-base: /* normal text font */
--vp-font-family-mono: /* code font */
}
警告
如果您使用的是可选组件(例如 团队页面 组件),请确保也从 vitepress/theme-without-fonts
导入它们!
如果您的字体是通过 @font-face
引用的本地文件,它将被处理为资产并包含在 .vitepress/dist/assets
下,文件名经过哈希处理。要预加载此文件,请使用 transformHead 构建挂钩
// .vitepress/config.js
export default {
transformHead({ assets }) {
// adjust the regex accordingly to match your font
const myFontFile = assets.find(file => /font-name\.\w+\.woff2/)
if (myFontFile) {
return [
[
'link',
{
rel: 'preload',
href: myFontFile,
as: 'font',
type: 'font/woff2',
crossorigin: ''
}
]
]
}
}
}
注册全局组件
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
/** @type {import('vitepress').Theme} */
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// register your custom global components
app.component('MyGlobalComponent' /* ... */)
}
}
如果您使用的是 TypeScript
// .vitepress/theme/index.ts
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// register your custom global components
app.component('MyGlobalComponent' /* ... */)
}
} satisfies Theme
由于我们使用的是 Vite,因此您还可以利用 Vite 的 全局导入功能 自动注册组件目录。
布局插槽
默认主题的 <Layout/>
组件有一些插槽,可用于在页面的特定位置注入内容。以下是如何将组件注入大纲之前的示例
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
extends: DefaultTheme,
// override the Layout with a wrapper component that
// injects the slots
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, {
'aside-outline-before': () => h(MyComponent)
})
}
}
默认主题布局中可用的插槽完整列表
- 当通过前置信息启用
layout: 'doc'
(默认)时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'
时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'
时page-top
page-bottom
- 在未找到(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
使用视图过渡 API
在外观切换时
您可以扩展默认主题以在颜色模式切换时提供自定义过渡。一个例子
<!-- .vitepress/theme/Layout.vue -->
<script setup lang="ts">
import { useData } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import { nextTick, provide } from 'vue'
const { isDark } = useData()
const enableTransitions = () =>
'startViewTransition' in document &&
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
if (!enableTransitions()) {
isDark.value = !isDark.value
return
}
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
)}px at ${x}px ${y}px)`
]
await document.startViewTransition(async () => {
isDark.value = !isDark.value
await nextTick()
}).ready
document.documentElement.animate(
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
{
duration: 300,
easing: 'ease-in',
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
}
)
})
</script>
<template>
<DefaultTheme.Layout />
</template>
<style>
::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 {
width: 22px !important;
}
.VPSwitchAppearance .check {
transform: none !important;
}
</style>
结果(**警告!**:闪烁的颜色、突然的移动、强烈的灯光)
演示
请参阅 Chrome 文档 以获取有关视图过渡的更多详细信息。
在路由更改时
即将推出。
覆盖内部组件
您可以使用 Vite 的 别名 将默认主题组件替换为您的自定义组件
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vitepress'
export default defineConfig({
vite: {
resolve: {
alias: [
{
find: /^.*\/VPNavBar\.vue$/,
replacement: fileURLToPath(
new URL('./components/CustomNavBar.vue', import.meta.url)
)
}
]
}
}
})
要了解组件的确切名称,请参阅 我们的源代码。由于这些组件是内部组件,因此它们在次要版本之间更新名称的可能性很小。