可能的bug: vitepress-plugin-breadcrumbs 的动态路由响应及url子路径支持
如题,目前发现vitepress-plugin-breadcrumbs似乎不会跟随页面的切换而自动更新,以及在vitepress处于子路径(如主页url类似vitepress.site/blog/)下时,vitepress-plugin-breadcrumbs似乎并没有配置选项能够应对这种情况;
目前是否有计划添加这两点特性?
你好,谢谢你的反馈,会添加第二点特性,但是第一点我还不太明白,能否更详细描述一下想要什么样的自动更新。
目前它的行为是这样的:
明白了,应该是我这边的配置有问题,我这边的vitepress(自行东拼西凑出来的)的面包屑确实不会像图中一样跟随实际情况自行更新,我找下我自己的问题
这是我这边用ai生成的在我这边能正常运行的面包屑(链接可用性检查有问题),希望能帮忙:
<template>
<div class="breadcrumb">
<span v-for="(item, index) in breadcrumbs" :key="index">
<a v-if="item.link" :href="item.link">{{ item.text }}</a>
<span v-else>{{ item.text }}</span>
<span v-if="index < breadcrumbs.length - 1" class="separator"> / </span>
</span>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useRoute, useData } from 'vitepress'
const route = useRoute()
const { site, theme, page } = useData()
// 获取所有可用路径
const availablePaths = computed(() => {
const paths = new Set()
// 从侧边栏收集所有有效链接
const collectLinks = (items) => {
items.forEach(item => {
if (item.link) paths.add(item.link)
if (item.items) collectLinks(item.items)
})
}
if (Array.isArray(theme.value.sidebar)) {
collectLinks(theme.value.sidebar)
} else {
Object.values(theme.value.sidebar || {}).forEach(section => {
if (Array.isArray(section)) {
collectLinks(section)
}
})
}
return paths
})
const breadcrumbs = computed(() => {
// 获取当前路径,确保不重复base
const path = route.path
const basePath = site.value.base || '/'
// 移除base前缀(如果存在)以避免重复
const relativePath = path.startsWith(basePath) && basePath !== '/'
? path.slice(basePath.length - 1)
: path
// 首页始终是第一个面包屑
const items = [
{
text: '首页',
link: basePath
}
]
// 如果不是首页,添加其他面包屑
if (relativePath !== '/') {
// 分割路径,创建面包屑层次
const segments = relativePath.split('/').filter(Boolean)
let currentPath = ''
segments.forEach((segment, index) => {
// 对URL编码的部分进行解码,正确显示中文
const decodedSegment = decodeURIComponent(segment)
currentPath += `/${segment}`
// 格式化段名称作为默认文本
let text = decodedSegment.charAt(0).toUpperCase() + decodedSegment.slice(1).replace(/-/g, ' ')
// 尝试从侧边栏找到更好的标题
const sidebarItem = findSidebarItem(theme.value.sidebar, currentPath)
if (sidebarItem) {
text = sidebarItem.text
}
// 对于最后一段,使用页面标题
if (index === segments.length - 1) {
text = page.value.title || text
}
// 检查路径是否可访问
const fullPath = basePath + currentPath.slice(1)
const isPathAvailable = isValidPath(fullPath)
items.push({
text,
// 如果是最后一项或路径不可访问,则不提供链接
link: index === segments.length - 1 ? null :
isPathAvailable ? fullPath : findFirstValidChildPath(currentPath)
})
})
}
return items
})
// 检查路径是否有效
function isValidPath(path) {
return availablePaths.value.has(path)
}
// 查找指定路径下的第一个有效子页面
function findFirstValidChildPath(parentPath) {
const validPaths = Array.from(availablePaths.value)
.filter(path => path.startsWith(parentPath + '/'))
.sort((a, b) => a.length - b.length)
return validPaths[0] || null
}
// 辅助函数:在侧边栏中查找项
function findSidebarItem(sidebar, path) {
if (!sidebar) return null
// 处理不同形式的侧边栏配置
const flattenSidebar = (items) => {
let result = []
items.forEach(item => {
if (item.link) result.push(item)
if (item.items) result = result.concat(flattenSidebar(item.items))
})
return result
}
let items = []
if (Array.isArray(sidebar)) {
items = flattenSidebar(sidebar)
} else {
// 处理对象形式的侧边栏
Object.values(sidebar).forEach(section => {
if (Array.isArray(section)) {
items = items.concat(flattenSidebar(section))
}
})
}
return items.find(item => {
// 移除可能的base路径进行比较
const itemPath = item.link
const basePath = site.value.base || '/'
const normalizedItemPath = itemPath.startsWith(basePath) && basePath !== '/'
? itemPath.slice(basePath.length - 1)
: itemPath
return normalizedItemPath === path
})
}
</script>
<style scoped>
.breadcrumb {
padding: 0.5rem 0;
margin-bottom: 1rem;
font-size: 0.9rem;
color: var(--vp-c-text-2);
}
.breadcrumb a {
color: var(--vp-c-brand);
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
.separator {
margin: 0 0.5rem;
}
</style>
食用方法:
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
'doc-before': () => h(Breadcrumb),
})
},
enhanceApp({ app, router }) {
app.component('Breadcrumb', Breadcrumb)
},
至于vitepress-plugin-breadcrumbs在我这边不正常的情况,后续我会尝试排查
辛苦了,非常感谢!
额我这试图尝试从头搭一个新的vitepress来试下:
pnpm add vitepress
pnpm vitepress init
┌ Welcome to VitePress!
│
◇ Where should VitePress initialize the config?
│ ./docs
│
◇ Site title:
│ My Awesome Project
│
◇ Site description:
│ A VitePress Site
│
◇ Theme:
│ Default Theme + Customization
│
◇ Use TypeScript for config and theme files?
│ Yes
│
◇ Add VitePress npm scripts to package.json?
│ Yes
│
└ Done! Now run pnpm run docs:dev and start writing.
Tips:
- Since you've chosen to customize the theme, you should also explicitly install vue as a dev dependency.
pnpm add -D vue
pnpm add @nolebase/vitepress-plugin-breadcrumbs
添加了对应的测试目录与手动目录配置
然后我把这个文件复制到项目根目录并去除了"paths"板块,
最后我参照这里进行了修改,
pnpm run docs:dev
最后发现不知为何breadcrumbs他不会自动更新,只会在手动刷新页面时才更新,编译(build)后再运行(preview)也是同样的现象,