Skip to content

VitePress 系列教程:05-内容展示优化

首页组件

如果我们觉得首页内容比较单调,想自己设计点可交互的内容,我们可以通过以下方案

追加样式内容

我们在首页 index.md 中添加内容

---
layout: home

hero:
  name: VitePress
  text: Vite & Vue powered static site generator.
  tagline: Lorem ipsum...
  image:
      src: /logo.svg
      alt: VitePress
  actions:
    - theme: brand
      text: Get Started
      link: /guide/what-is-vitepress
    - theme: alt
      text: View on GitHub
      link: https://github.com/vuejs/vitepress

features:
  - icon: ⚡️
    title: Vite, The DX that can't be beat
    details: Lorem ipsum...
  - icon: 🖖
    title: Power of Vue meets Markdown
    details: Lorem ipsum...
  - icon: 🛠️
    title: Simple and minimal, always
    details: Lorem ipsum...

---

添加的内容是能直接渲染到首页的 Features 下,Footer 上的空白部分的,由于 vitepress 使用的是 markdown-it 解析的 md,所以也能直接写 html 标签

html
<div style="color: red; font-size: 24px;">这是个有 style 的随便写点</div>

尝试着加点 class 属性,让内容更丰富

html
<div class="demo">这是个有 style 的随便写点</div>

然后在文档后补添加内存

html
<style>
    .demo {
        font-weight: 700;
        font-size: 64px;
    }
</style>

写完之后可以发现内容变化了。

TIP

还可以使用 tailwind css 之类预设样式类,需要安装拓展插件才可以。

Vue SFC 组件

我们可以自己写一个 SFC ,注册到工程中,然后在 md 中使用

1、在 theme 目录中创建 components 目录,然后创建 Counter.vue

vue
<script setup>
import {ref} from 'vue'

const num = ref(0)
const add = () => {
  num.value++
}
</script>

<template>
  <div class="counter">我就是 counter 组件 </div>
  <button class="btn" @click="add">{{ num }}</button>
</template>

<style>
.counter {
  color: blue;
  font-size: 24px;
  font-weight: 600;
}

.btn {
  width: 50px;
  height: 50px;
  border: 1px solid green;
}
</style>

2、在 .vitepress/theme/index.js 中注册 Counter.vue 组件

js
import Theme from 'vitepress/theme'
import './style/var.css'
import Counter from './components/Counter.vue'

export default {
    ...Theme,
    enhanceApp({app}) {
        app.component('Counter', Counter)
    }
}

3、在首页 index.md 使用组件

<Counter></Counter>

可以看到 Counter 组件成功被渲染出来,并且点击按钮能响应式变化数字

所以,对于复杂的交互组件,我们可以通过自定义 SFC,然后在 theme/index.js 中注册组件,并在 md 中使用组件,达到想要的复杂交互效果

使用首页预留插槽

现在我们已经能做到在 Features 下 Footer 上添加自定义内容了,但是我有办法将自定义内容加到 Header 下 Hero 上吗

答案是可以的,vitepress 首页给我们预留了很多插槽,通过插槽我们可以将自定义组件渲染到想要的位置

使用插槽案例

尝试将一个组件放到 Hero 上方

1、在 components 目录下新建 HeroBefore.vue

vue
<script setup>

</script>

<template>
  <div class="before">HeroBefore</div>
</template>

<style>
.before {
  color: green;
  font-size: 24px;
  font-weight: 700;
  text-align: center;
}
</style>

2、安装 vue,因为需要使用 vue 提供的 h 方法

npm add vue -D

3、在 theme/index.ts 中使用插槽

js
import Theme from 'vitepress/theme'
import './style/var.css'
import FreeStyle from './components/FreeStyle.vue'
import {h} from 'vue'
import HeroBefore from './components/HeroBefore.vue'

export default {
    ...Theme,
    Layout() {
        return h(Theme.Layout, null, {
            'home-hero-before': () => h(HeroBefore)
        })
    },
    enhanceApp({app}) {
        app.component('FreeStyle', FreeStyle)
    }
}

组件已经渲染到 Header 下 Hero 上方了

查看插槽位置

vitepress 文档并没有详细说明,我们可以通过查阅 vitepress 源码来知道预留的插槽位置,文件在 src/client/theme-default/components/Layout.vue

vue
<template>
  <div class="Layout">
    <slot name="layout-top"/>
    <VPSkipLink/>
    <VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar"/>
    <VPNav>
      <template #nav-bar-title-before>
        <slot name="nav-bar-title-before"/>
      </template>
      <template #nav-bar-title-after>
        <slot name="nav-bar-title-after"/>
      </template>
      <template #nav-bar-content-before>
        <slot name="nav-bar-content-before"/>
      </template>
      <template #nav-bar-content-after>
        <slot name="nav-bar-content-after"/>
      </template>
      <template #nav-screen-content-before>
        <slot name="nav-screen-content-before"/>
      </template>
      <template #nav-screen-content-after>
        <slot name="nav-screen-content-after"/>
      </template>
    </VPNav>
    <VPLocalNav :open="isSidebarOpen" @open-menu="openSidebar"/>
    <VPSidebar :open="isSidebarOpen"/>

    <VPContent>
      <template #home-hero-before>
        <slot name="home-hero-before"/>
      </template>
      <template #home-hero-after>
        <slot name="home-hero-after"/>
      </template>
      <template #home-features-before>
        <slot name="home-features-before"/>
      </template>
      <template #home-features-after>
        <slot name="home-features-after"/>
      </template>

      <template #doc-footer-before>
        <slot name="doc-footer-before"/>
      </template>
      <template #doc-before>
        <slot name="doc-before"/>
      </template>
      <template #doc-after>
        <slot name="doc-after"/>
      </template>

      <template #aside-top>
        <slot name="aside-top"/>
      </template>
      <template #aside-bottom>
        <slot name="aside-bottom"/>
      </template>
      <template #aside-outline-before>
        <slot name="aside-outline-before"/>
      </template>
      <template #aside-outline-after>
        <slot name="aside-outline-after"/>
      </template>
      <template #aside-ads-before>
        <slot name="aside-ads-before"/>
      </template>
      <template #aside-ads-after>
        <slot name="aside-ads-after"/>
      </template>
    </VPContent>

    <VPFooter/>
    <slot name="layout-bottom"/>
  </div>
</template>

通过插槽名能大概猜到位置在哪,当然也能一个个试知道具体位置,结合这些插槽就能自定义出更个性化的 vitepress 首页了

更改首页标题色调

默认首页展示的标题颜色是绿色,图标背景是白色,通过以下操作,可以获得跟官方官网一样的炫彩配色了

1、在 .vitepress 目录下创建 theme 目录,theme 目录下创建 index.ts ,输入以下内容

js
import Theme from 'vitepress/theme'

export default {
  ...Theme
}

2、在 theme 目录下创建 style 目录,style 目录下创建 var.css

js
:root {
  --vp-home-hero-name-color: red;
}

3、在 theme/index.ts 下引入 style/var.css

js
import Theme from 'vitepress/theme'
import './style/var.css'

export default {
  ...Theme
}

可以看到标题颜色已经变成设定的红色了

可以加点渐变色来让整体效果好看点,渐变色可以从 这个网站 获取

css
:root {
    /* 标题 */
    --vp-home-hero-name-color: transparent;
    --vp-home-hero-name-background: linear-gradient(135deg, #F6CEEC 10%, #D939CD 100%);

    /* 图标背景 */
    --vp-home-hero-image-background-image: linear-gradient(135deg, #F6CEEC 10%, #D939CD 100%);
    --vp-home-hero-image-filter: blur(150px);
}

首页颜色源码解读

我们通过使用 var.css 文件,给根节点 root 添加了css 变量,来改变首页的标题和图片的背景色, 那我们要怎么确认用什么属性就能修改我希望修改的元素呢

方法一:开发者工具

通过控制台我们就能直观的看出希望修改的样式有没有使用 css 变量,以图片背景做例子

在开发者工具中,可以看到

css
.image-bg {
    background-image: var(--vp-home-hero-image-background-image);
    filter: var(--vp-home-hero-image-filter);
}

这两个变量就是我们通过 root 下注入的 css 变量,因为我们显式的修改了两个变量,所以系统优先使用我们设定的样式

方案二:看源码

拉取 vitepress 的源码,看到 src/client/theme-default/Layout.vue ,这个 sfc 就是文档的布局组件,三种 layout 模式都是使用的这个组件

vue
<script setup lang="ts">
import {computed, provide, useSlots, watch} from 'vue'
import {useRoute} from 'vitepress'
import {useData} from './composables/data.js'
import {useSidebar, useCloseSidebarOnEscape} from './composables/sidebar.js'
import VPSkipLink from './components/VPSkipLink.vue'
import VPBackdrop from './components/VPBackdrop.vue'
import VPNav from './components/VPNav.vue'
import VPLocalNav from './components/VPLocalNav.vue'
import VPSidebar from './components/VPSidebar.vue'
import VPContent from './components/VPContent.vue'
import VPFooter from './components/VPFooter.vue'

const {
  isOpen: isSidebarOpen,
  open: openSidebar,
  close: closeSidebar
} = useSidebar()

const route = useRoute()
watch(() => route.path, closeSidebar)

useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)

provide('close-sidebar', closeSidebar)
provide('is-sidebar-open', isSidebarOpen)

const {frontmatter} = useData()

const slots = useSlots()
const heroImageSlotExists = computed(() => !!slots['home-hero-image'])

provide('hero-image-slot-exists', heroImageSlotExists)
</script>

<template>
  <div v-if="frontmatter.layout !== false" class="Layout">
    <slot name="layout-top"/>
    <VPSkipLink/>
    <VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar"/>
    <VPNav>
      <template #nav-bar-title-before>
        <slot name="nav-bar-title-before"/>
      </template>
      <template #nav-bar-title-after>
        <slot name="nav-bar-title-after"/>
      </template>
      <template #nav-bar-content-before>
        <slot name="nav-bar-content-before"/>
      </template>
      <template #nav-bar-content-after>
        <slot name="nav-bar-content-after"/>
      </template>
      <template #nav-screen-content-before>
        <slot name="nav-screen-content-before"/>
      </template>
      <template #nav-screen-content-after>
        <slot name="nav-screen-content-after"/>
      </template>
    </VPNav>
    <VPLocalNav :open="isSidebarOpen" @open-menu="openSidebar"/>

    <VPSidebar :open="isSidebarOpen">
      <template #sidebar-nav-before>
        <slot name="sidebar-nav-before"/>
      </template>
      <template #sidebar-nav-after>
        <slot name="sidebar-nav-after"/>
      </template>
    </VPSidebar>

    <VPContent>
      <template #home-hero-before>
        <slot name="home-hero-before"/>
      </template>
      <template #home-hero-image>
        <slot name="home-hero-image"/>
      </template>
      <template #home-hero-after>
        <slot name="home-hero-after"/>
      </template>
      <template #home-features-before>
        <slot name="home-features-before"/>
      </template>
      <template #home-features-after>
        <slot name="home-features-after"/>
      </template>

      <template #doc-footer-before>
        <slot name="doc-footer-before"/>
      </template>
      <template #doc-before>
        <slot name="doc-before"/>
      </template>
      <template #doc-after>
        <slot name="doc-after"/>
      </template>

      <template #aside-top>
        <slot name="aside-top"/>
      </template>
      <template #aside-bottom>
        <slot name="aside-bottom"/>
      </template>
      <template #aside-outline-before>
        <slot name="aside-outline-before"/>
      </template>
      <template #aside-outline-after>
        <slot name="aside-outline-after"/>
      </template>
      <template #aside-ads-before>
        <slot name="aside-ads-before"/>
      </template>
      <template #aside-ads-after>
        <slot name="aside-ads-after"/>
      </template>
    </VPContent>

    <VPFooter/>
    <slot name="layout-bottom"/>
  </div>
  <Content v-else/>
</template>

<style scoped>
.Layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
</style>

看到 VPContent 组件,这里会通过 frontmatter.layout 来切换使用的布局模式,所以通过这个文件我们可以看到,三种模式对应的组件名为

  • doc:VPDoc
  • page:VPPage
  • home:VPHome

我们主要看 VPHome 组件

拓展,vitepress 使用的读取 md 头部信息所使用的插件是 gray-matter ,感兴趣的可以查阅下使用方法

vue
<script setup lang="ts">
import VPHomeHero from './VPHomeHero.vue'
import VPHomeFeatures from './VPHomeFeatures.vue'
</script>

<template>
  <div class="VPHome">
    <slot name="home-hero-before"/>
    <VPHomeHero>
      <template #home-hero-image>
        <slot name="home-hero-image"/>
      </template>
    </VPHomeHero>
    <slot name="home-hero-after"/>

    <slot name="home-features-before"/>
    <VPHomeFeatures/>
    <slot name="home-features-after"/>

    <Content/>
  </div>
</template>

<style scoped>
.VPHome {
  padding-bottom: 96px;
}

.VPHome :deep(.VPHomeSponsors) {
  margin-top: 112px;
  margin-bottom: -128px;
}

@media (min-width: 768px) {
  .VPHome {
    padding-bottom: 128px;
  }
}
</style>

这里就是首页模式下的布局情况,可以看到组件名就是对应的我们在 index.md 中设置的 herofeatures

关于首页标题和图标的样式在 VPHomeHero 组件中

做个预告,可以看到下面有个 Content 组件,可以自定义首页下半部分的内容,将会是下篇文章讲的内容,敬请期待~

vue
<script setup lang="ts">
import {useData} from '../composables/data.js'
import VPHero from './VPHero.vue'

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

<template>
  <VPHero
      v-if="fm.hero"
      class="VPHomeHero"
      :name="fm.hero.name"
      :text="fm.hero.text"
      :tagline="fm.hero.tagline"
      :image="fm.hero.image"
      :actions="fm.hero.actions"
  >
    <template #home-hero-image>
      <slot name="home-hero-image"/>
    </template>
  </VPHero>
</template>

然后在 VPHero 中的源码中可以看到首页布局的真面目了!我们先看到标题的 css 属性

css
.name {
    color: var(--vp-home-hero-name-color);
}

.clip {
    background: var(--vp-home-hero-name-background);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: var(--vp-home-hero-name-color);
}

可以明显看到这里使用的css变量,就是我们在var.css中设定的css变量

同理,我们看图片的 css 属性

css
.image-bg {
    position: absolute;
    top: 50%;
    left: 50%;
    border-radius: 50%;
    width: 192px;
    height: 192px;
    background-image: var(--vp-home-hero-image-background-image);
    filter: var(--vp-home-hero-image-filter);
    transform: translate(-50%, -50%);
}

也是我们在 var.css 中设定的 css 变量

所以,我们可以直接在 VitePress 源码中,找到我们希望更改样式的组件,观察他们的 css 样式是否使用 css 变量,然后我们在 var.css 中进行更改即可

我们根据这个方法,改一下首页的按钮样式

实践,更改首页按钮样式

首页的按钮通过 hero 下的 actions 属性控制,通过 actions.theme 控制样式,默认是 brand ,也就是绿色按钮,总共有三种模式:brandaltsponsor

VPButton 源码

vue
<script setup lang="ts">
import {computed} from 'vue'
import {normalizeLink} from '../support/utils.js'
import {EXTERNAL_URL_RE} from '../../shared.js'

const props = defineProps<{
  tag?: string
  size?: 'medium' | 'big'
  theme?: 'brand' | 'alt' | 'sponsor'
  text: string
  href?: string
}>()

const classes = computed(() => [
  props.size ?? 'medium',
  props.theme ?? 'brand'
])

const isExternal = computed(() => props.href && EXTERNAL_URL_RE.test(props.href))

const component = computed(() => {
  if (props.tag) {
    return props.tag
  }

  return props.href ? 'a' : 'button'
})
</script>

<template>
  <component
      :is="component"
      class="VPButton"
      :class="classes"
      :href="href ? normalizeLink(href) : undefined"
      :target="isExternal ? '_blank' : undefined"
      :rel="isExternal ? 'noreferrer' : undefined"
  >
    {{ text }}
  </component>
</template>

通过分析源码,可以看到 button 的样式控制,通过传入的 theme ,计算动态 classes ,然后传给组件

css
.VPButton.brand {
    border-color: var(--vp-button-brand-border);
    color: var(--vp-button-brand-text);
    background-color: var(--vp-button-brand-bg);
}

.VPButton.brand:hover {
    border-color: var(--vp-button-brand-hover-border);
    color: var(--vp-button-brand-hover-text);
    background-color: var(--vp-button-brand-hover-bg);
}

.VPButton.brand:active {
    border-color: var(--vp-button-brand-active-border);
    color: var(--vp-button-brand-active-text);
    background-color: var(--vp-button-brand-active-bg);
}

这里就是 brand 模式下的 button 样式,可以看到使用了三个 css 变量,我们在 var.css 中对着三个样式进行改动

css
/* var.css */
:root {
    /* 标题 */
    --vp-home-hero-name-color: transparent;
    --vp-home-hero-name-background: linear-gradient(135deg, #F6CEEC 10%, #D939CD 100%);

    /* 图标背景 */
    --vp-home-hero-image-background-image: linear-gradient(135deg, #F6CEEC 10%, #D939CD 100%);
    --vp-home-hero-image-filter: blur(150px);

    /* brand按钮 */
    --vp-button-brand-border: #F6CEEC;
    --vp-button-brand-text: #F6CEEC;
    --vp-button-brand-bg: #D939CD;

    --vp-button-brand-hover-border: #F6CEEC;
    --vp-button-brand-hover-text: #fff;
    --vp-button-brand-hover-bg: #D939CD;

    --vp-button-brand-active-border: #F6CEEC;
}