Skip to content

使用 slot 在页面显示组件

VitePress 的缺省主题提供了一组 slot,我们可以往其中插入想要的元素。参考文档:https://vitepress.dev/guide/extending-default-theme#layout-slots

在所有 Doc 页面标题上方加入组件

1.新建一个组件,用于显示文章标题

在这个组件中,我们使用useData()来获取标题,并显示在组件中。

vue
<!--./.vitepress/theme/components/DocHeroTitle.vue-->
<script setup>
import { useData } from 'vitepress'
const {page} = useData()
</script>

<template>
   <div class="box">
    <slot>{{ page.title}}</slot>
   </div>
</template>

<style scoped>
.box {
    @apply mb-16 h-[200px] rounded-2xl;
    @apply bg-purple-500 text-white font-bold;
    @apply flex items-center justify-center;
    @apply text-2xl md:text-6xl;
}
</style>

2. 在 index.ts 中配置 slot

ts
import { h } from "vue"
import DocHeroTitle from './components/DocHeroTitle.vue'

export default {
  extends: DefaultTheme,
  Layout: () => {
    return h(DefaultTheme.Layout, null, {
      "doc-before": () => h(DocHeroTitle),
    })
  },
} satisfies Theme

如果你不熟悉 Vue 语法, 不妨了解下:h 函数是 createElement 函数的别名,它是用于创建和返回虚拟节点(VNodes)的函数。h 函数是从 vue 包中导入的主要函数。

实际效果如下:

根据 frontmatter,在页面顶部中加入元素

我们想要的效果是,如下设置 frontmatter,然后自动显示:

markdown
---
herotag: Crypto + AI
---

1. 创建组件

vue
<!--./.vitepress/theme/components/DocHeroTag.vue-->

<script setup>
import { useData } from 'vitepress'
const {frontmatter} = useData()
</script>

<template>
    <DocHero 
        v-if="frontmatter.herotag"
        :herobg="frontmatter.herobg"> 
        {{frontmatter.herotag}} 
    </DocHero>
</template>

其中我们用到如下组件DocHero

DocHero component
vue
<script setup lang="ts">
import { computed } from 'vue'

const props = defineProps<{
    number?: string,
    herobg?: string | number,   // herobg, default, 1, 2
    small?: boolean 
}>()

const bg = computed(() => {
  if (!props.herobg) return '0'
  return String(props.herobg)
})

const bgImage = computed(() => {
  return `url('/gradient-bg${bg.value === '0' ? '' : `-${bg.value}`}.jpg')`
})
</script>

<template>
<div class="titlebox">
        <div class="titlehero">
            <slot ></slot>
        </div>

        <div v-if="number" class="number">
            #{{number}}
            <div class="grid grid-cols-3 h-1 -mt-2">
              <div class="bg-orange-500"></div>
              <div class="bg-orange-300"></div>
              <div class="bg-orange-100"></div>
            </div>
        </div>
</div>
</template>

<style scoped>
.titlebox{
    @apply mb-16 h-[160px] md:h-[200px] rounded-lg text-center text-white;
    @apply flex items-center justify-center;
    @apply text-[50px] md:text-[80px] font-extrabold;
    @apply relative z-10 bg-orange-500;

    background-image:  v-bind('bgImage');

    /* background-image: v-bind('bg === "1" ? "url(\'/gradient-bg-1.jpg\')" : "url(\'/gradient-bg.jpg\')"'); */
}

.titlehero{
    /* @apply relative h-2/3 mt-20; */
    @apply relative h-2/3 mt-4;
}

.titlehero::after {
    content: "";
    position: absolute;
    /* top: 15%; */
    top: 50%;
    left: 0;
    right: 0;
    height: 30%;
    background-color: rgba(255, 0, 0, 1);
    z-index: -1;
    border-radius: 2px;
}

.number{
    @apply absolute bottom-2 right-3 text-[24px] md:text-[40px] font-normal;
}
</style>

2. 在 index.ts 中插入 slot

在 VPDoc 的 doc-before slot 中插入,该组件会

ts
export default {
  extends: DefaultTheme,

//...

  Layout: () => {
    return h(DefaultTheme.Layout, null, {
      "doc-before": () => h(DocHeroTag),
    })
  },  
} satisfies Theme

3. 在 markdown 文档中加入相应的 tag

markdown
---
herotag: Crypto + AI
---

Alang.AI - Make Great AI Applications