Skip to content

VitePress 文章顶部面包屑

基本逻辑

扩展默认主题,并通过 VitePress 内置主题提供的 doc-before 布局插槽,把面包屑 Vue 组件塞进去。面包屑组件通过 useData() API 获取当前页面路径并用 v-for 渲染。

代码实现

ts
import DefaultTheme from "vitepress/theme";
import Layout from "./layout.vue";

export default <Theme>{
  extends: DefaultTheme,
  Layout,
  // ...
};
vue
<template>
  <Layout>
    <template #doc-before>
      <Breadcrumb />
    </template>
    <!-- ... -->
  </Layout>
</template>

<script setup lang="ts">
import Breadcrumb from "./components/breadcrumb.vue";
// ...
</script>
vue
<template>
  <div id="breadcrumb">
    <span
      v-for="item in items"
      class="bc-items"
      :id="item.first ? 'bc-project' : undefined"
      >{{ item.name }}</span
    >
  </div>
</template>

<script setup lang="ts">
import { useData } from "vitepress";
const { page } = useData();
import { ref, watchEffect } from "vue";
import pangu from "pangu";

type Breadcrumb = {
  name: string;
  first: boolean;
};

const items = ref<Breadcrumb[]>([]);
watchEffect(() => {
  const pathSegs = page.value.filePath.split("/");
  const shownSegs =
    pathSegs.at(-1) === "index.md"
      ? pathSegs.slice(0, -2)
      : pathSegs.slice(0, -1);
  // 面包屑只显示到当前页面的上一级,不包含页面标题
  // 如果是首页,则一并去除当前目录名

  items.value = shownSegs.map((item, index) => ({
    name: pangu.spacing(item.replaceAll("-", " ")),
    first: !index,
  }));
});
</script>

<style>
#breadcrumb {
  margin-bottom: 15px;
  font-size: 15px;
  color: var(--vp-c-text-2);
}
.bc-items::after {
  content: "/";
  margin: 0 8px;
  vertical-align: top;
  font-weight: bold;
  font-size: 10px;
  color: var(--vp-c-text-3);
}
</style>

相关文档