开启并自定义基于内容目录的顶栏导航。
Header 导航
如果你的站点布局需要把内容目录映射到顶栏,可以在 .config.yml 里显式开启:
# content/docs/guide/.config.yml
navigation:
label: 指南
header:
enable: true常见用法是只给一级目录开启 header.enable,这样顶栏就显示一级栏目;如果你还给子目录也开启,它们会继续作为下一级 tree 节点参与渲染。
目前内置 schema 支持:
| 字段 | 类型 | 说明 |
|---|---|---|
header.enable | boolean | 设为 true 时,把当前目录加入 header 导航树。 |
header.label | string | 覆盖 header 中显示的标题,不影响侧边栏 navigation.label。 |
用内置 LayoutNav 渲染 header
LayoutNav 会自动读取 pageContext.data.headerNav 并派生 currentPath,直接在布局里放一个即可:
<script setup>
import { LayoutNav } from 'vike-vue-content/components/layout-nav'
</script>
<template>
<header>
<LayoutNav />
</header>
</template>items 和 current-path 都是可选的。只有当你需要覆盖默认行为(比如过滤导航项或自定义路径匹配策略)时,才需要显式传入。
自定义 header 时怎么取数据
如果你不想直接用 LayoutNav,也可以自己消费同一份数据。docs 数据加载器会把 header 树放到 pageContext.data.headerNav 里:
type DocsHeaderNavItem = {
title: string
path: string
matchPath: string
children?: DocsHeaderNavItem[]
}下面是一个最小可运行的自定义 header 示例,它直接从 pageContext.data 读取,再渲染成你自己的顶栏:
Docs
这个示例直接消费 pageContext.data.headerNav,不依赖内置 LayoutNav。
index.vue
<template>
<div class="demo-header-shell">
<header class="demo-header">
<div class="demo-header-brand">
Docs
</div>
<nav v-if="headerNav.length" class="demo-header-nav" aria-label="Custom header demo">
<Link
v-for="item in headerNav"
:key="item.path"
:href="item.path"
class="demo-header-link"
:class="{ 'is-active': isActive(item) }"
data-vike="false"
>
{{ item.title }}
</Link>
</nav>
</header>
<p class="demo-header-note">
这个示例直接消费 <code>pageContext.data.headerNav</code>,不依赖内置 <code>LayoutNav</code>。
</p>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { usePageContext } from 'vike-vue/usePageContext'
import type { DocsHeaderNavItem } from 'vike-vue-content/shared/types'
import { Link } from 'vike-vue-content/components/link'
const pageContext = usePageContext()
const headerNav = computed<DocsHeaderNavItem[]>(() => {
const data = pageContext.data as { headerNav?: DocsHeaderNavItem[] } | undefined
return data?.headerNav ?? []
})
const currentPath = computed(() => {
const pathname = pageContext.urlPathname
const base = (pageContext as { _baseServer?: string })._baseServer ?? '/'
if (base === '/' || !pathname.startsWith(base)) {
return pathname
}
const trimmed = pathname.slice(base.length)
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`
})
function isActive(item: DocsHeaderNavItem) {
return (
currentPath.value === item.matchPath
|| currentPath.value.startsWith(`${item.matchPath}/`)
)
}
</script>
<style scoped>
.demo-header-shell {
background:
radial-gradient(circle at top left, color-mix(in srgb, var(--color-primary) 12%, transparent), transparent 42%),
linear-gradient(180deg, color-mix(in srgb, var(--color-surface) 88%, white), var(--color-bg));
border: 1px solid var(--color-border);
border-radius: calc(var(--radius) + 0.5rem);
padding: 1rem;
}
.demo-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
}
.demo-header-brand {
color: var(--color-text);
font-size: 1rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.demo-header-nav {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.demo-header-link {
color: var(--color-text-muted);
text-decoration: none;
padding: 0.55rem 0.85rem;
border-radius: 999px;
transition: background-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}
.demo-header-link:hover {
color: var(--color-text);
background-color: var(--color-surface-elevated);
}
.demo-header-link.is-active {
color: var(--color-primary);
background-color: color-mix(in srgb, var(--color-primary) 10%, var(--color-bg));
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 22%, transparent);
font-weight: 600;
}
.demo-header-note {
margin: 0.875rem 0 0;
color: var(--color-text-muted);
font-size: 0.925rem;
line-height: 1.6;
}
.demo-header-note code {
background-color: var(--color-surface-elevated);
border-radius: 0.35rem;
padding: 0.1rem 0.35rem;
}
</style>
说明:
headerNav只包含显式开启了header.enable的目录节点- 如果子目录也开启了
header.enable,它们会出现在children里,适合做二级菜单 - 侧边栏仍然使用
navigation树;header只是额外派生出一棵顶栏导航树 - 你可以把这个 demo 的实现搬到自己的
+Layout.vue,然后改造成 dropdown、tabs 或 drawer