自定义导航栏

2431 字
12 分钟
自定义导航栏
AI 概括

点击按钮,AI 将为你生成这篇文章的摘要

自定义导航栏#

本文档详细介绍如何为 Firefly 博客添加自定义导航栏菜单项,以”说说 (Talks)“功能为例。


目录#

  1. 目录结构总览
  2. Step 1:创建内容集合
  3. Step 2:创建 Markdown 文件
  4. Step 3:添加枚举值
  5. Step 4:添加页面开关
  6. Step 5:添加导航栏配置
  7. Step 6:添加预设映射
  8. Step 7:添加国际化键值
  9. Step 8:创建页面路由
  10. 注意事项
  11. 快速参考模板

目录结构总览#

src/
├── content/
│ ├── content.config.ts # 内容集合定义
│ └── spec/
│ └── talks.md # ✅ 说说内容文件
├── types/
│ └── config.ts # ✅ LinkPreset 枚举定义
├── constants/
│ └── link-presets.ts # ✅ 预设映射表
├── i18n/
│ ├── i18nKey.ts # ✅ 国际化键值
│ ├── translation.ts # 国际化翻译
│ └── languages/
│ ├── zh_CN.ts # ✅ 中文翻译
│ ├── en.ts # ✅ 英文翻译
│ ├── ja.ts # ✅ 日文翻译
│ ├── zh_TW.ts # ✅ 繁体中文翻译
│ └── ru.ts # ✅ 俄文翻译
├── config/
│ ├── siteConfig.ts # ✅ 页面开关
│ └── navBarConfig.ts # ✅ 导航栏配置
├── pages/
│ └── talks.astro # ✅ 页面路由
└── components/
└── layout/
└── Navbar.astro # 导航栏组件(无需修改)

Step 1:创建内容集合#

文件src/content.config.ts

在文件中添加 spec 集合定义(如果还没有的话):

import { defineCollection } from "astro:content";
import { glob } from "astro/loaders";
import { z } from "astro/zod";
const postsCollection = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/posts" }),
schema: z.object({
title: z.string(),
published: z.date(),
updated: z.date().optional(),
draft: z.boolean().optional().default(false),
description: z.string().optional().default(""),
image: z.string().optional().default(""),
tags: z.array(z.string()).optional().default([]),
category: z.string().optional().nullable().default(""),
lang: z.string().optional().default(""),
pinned: z.boolean().optional().default(false),
author: z.string().optional().default(""),
sourceLink: z.string().optional().default(""),
licenseName: z.string().optional().default(""),
licenseUrl: z.string().optional().default(""),
comment: z.boolean().optional().default(true),
password: z.string().optional().default(""),
passwordHint: z.string().optional().default(""),
abbrlink: z.string().optional().default(""),
prevTitle: z.string().default(""),
prevSlug: z.string().default(""),
nextTitle: z.string().default(""),
nextSlug: z.string().default(""),
}),
});
// ✅ spec 集合 - 用于说说、相册等特殊页面
const specCollection = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/spec" }),
schema: z.object({}), // schema 为空,灵活度高
});
export const collections = {
posts: postsCollection,
spec: specCollection,
};

Step 2:创建 Markdown 文件#

文件src/content/spec/talks.md

创建说说内容文件:

---
title: 说说
date: 2026-05-27
---
# 我的说说
这里可以写你的日常动态、碎碎念,支持所有 Markdown 语法:
- 2026-05-27:终于把博客导航栏的说说菜单配置好了!
- 2026-05-26:相册的加密功能终于跑通了,开心~
- 2026-05-25:今天开始学习 Astro 博客搭建,加油!
> 以后可以在这里随时更新,内容和页面完全分离,维护超方便~

说明

  • Frontmatter 字段可选(spec 集合 schema 为空)
  • 建议保留 titledate 便于管理
  • 支持标准 Markdown 语法

Step 3:添加枚举值#

文件src/types/config.ts

LinkPreset 枚举中添加新值:

export enum LinkPreset {
Home = 0,
Archive = 1,
About = 2,
Friends = 3,
Sponsor = 4,
Guestbook = 5,
Bangumi = 6,
Gallery = 7,
Talk = 8, // ✅ 添加说说枚举值
}

注意事项

  • 枚举值必须唯一(不能与其他值重复)
  • 命名要简洁明了(Talk 而不是 Talks
  • 后续所有引用必须保持一致

Step 4:添加页面开关#

文件src/config/siteConfig.ts

pages 对象中添加开关:

export const siteConfig = {
// ... 其他配置
pages: {
home: true,
archive: true,
about: true,
friends: true,
sponsor: true,
guestbook: true,
bangumi: true,
gallery: true,
talks: true, // ✅ 添加说说开关
// ...
}
}

说明

  • true = 在导航栏显示
  • false = 在导航栏隐藏
  • 开关关闭时,导航栏和页面都不会生成

Step 5:添加导航栏配置#

文件src/config/navBarConfig.ts

getDynamicNavBarConfig 函数中添加菜单项:

const getDynamicNavBarConfig = (): NavBarConfig => {
const links: (NavBarLink | LinkPreset)[] = [
LinkPreset.Home,
LinkPreset.Archive,
];
// 根据配置决定是否添加留言板
if (siteConfig.pages.guestbook) {
links.push(LinkPreset.Guestbook);
}
// 我的及其子菜单
links.push({
name: "动态",
url: "/my/",
icon: "material-symbols:person",
children: [
// 根据配置决定是否添加相册
...(siteConfig.pages.gallery ? [LinkPreset.Gallery] : []),
// ✅ 根据配置决定是否添加说说
...(siteConfig.pages.talks ? [LinkPreset.Talk] : []),
// 根据配置决定是否添加番组计划
...(siteConfig.pages.bangumi ? [LinkPreset.Bangumi] : []),
],
});
// 关于及其子菜单
links.push({
name: "关于",
url: "/content/",
icon: "material-symbols:info",
children: [
...(siteConfig.pages.sponsor ? [LinkPreset.Sponsor] : []),
...(siteConfig.pages.friends ? [LinkPreset.Friends] : []),
LinkPreset.About,
],
});
return { links } as NavBarConfig;
};

说明

  • 使用展开运算符 ... 根据开关决定是否添加
  • 可以放在 children 数组中作为子菜单
  • 也可以直接放在 links 数组中作为一级菜单

Step 6:添加预设映射#

文件src/constants/link-presets.ts

LinkPresets 对象中添加映射:

import I18nKey from "@i18n/i18nKey";
import { i18n } from "@i18n/translation";
import { LinkPreset, type NavBarLink } from "@/types/config";
export const LinkPresets: { [key in LinkPreset]: NavBarLink } = {
[LinkPreset.Home]: {
name: i18n(I18nKey.home),
url: "/",
icon: "material-symbols:home",
},
[LinkPreset.About]: {
name: i18n(I18nKey.about),
url: "/about/",
icon: "material-symbols:person",
},
[LinkPreset.Archive]: {
name: i18n(I18nKey.archive),
url: "/archive/",
icon: "material-symbols:archive",
},
[LinkPreset.Friends]: {
name: i18n(I18nKey.friends),
url: "/friends/",
icon: "material-symbols:group",
},
[LinkPreset.Sponsor]: {
name: i18n(I18nKey.sponsor),
url: "/sponsor/",
icon: "material-symbols:favorite",
},
[LinkPreset.Guestbook]: {
name: i18n(I18nKey.guestbook),
url: "/guestbook/",
icon: "material-symbols:chat",
},
[LinkPreset.Bangumi]: {
name: i18n(I18nKey.bangumi),
url: "/bangumi/",
icon: "material-symbols:movie",
},
[LinkPreset.Gallery]: {
name: i18n(I18nKey.gallery),
url: "/gallery/",
icon: "material-symbols:photo-library",
},
// ✅ 添加说说预设映射
[LinkPreset.Talk]: {
name: i18n(I18nKey.talks),
url: "/talks/",
icon: "material-symbols:chat",
},
};

字段说明

  • name:显示的菜单名称(通过 i18n 国际化)
  • url:点击后跳转的路径
  • icon:菜单图标(使用 Material Symbols 图标)

Step 7:添加国际化键值#

文件src/i18n/i18nKey.ts

在枚举中添加国际化键值:

export enum I18nKey {
// ... 其他键值
home = "home",
archive = "archive",
about = "about",
friends = "friends",
sponsor = "sponsor",
guestbook = "guestbook",
bangumi = "bangumi",
gallery = "gallery",
talks = "talks", // ✅ 说说标题
talksDescription = "talksDescription", // ✅ 说说描述
// ...
}

注意:需要同时添加标题和描述两个键值。


Step 8:创建页面路由#

文件src/pages/talks.astro

创建说说页面:

---
import MainGridLayout from "@/layouts/MainGridLayout.astro";
import { getEntry, render } from "astro:content";
import Markdown from "@components/common/Markdown.astro";
import I18nKey from "@/i18n/i18nKey";
import { i18n } from "@/i18n/translation";
// 读取 src/content/spec/talks.md
const talksPost = await getEntry("spec", "talks");
// 检查页面是否可用
if (!talksPost) {
throw new Error("Talks page content not found");
}
const { Content } = await render(talksPost);
---
<MainGridLayout
title={i18n(I18nKey.talks)}
description={i18n(I18nKey.talksDescription)}
>
<div class="flex w-full rounded-(--radius-large) overflow-hidden">
<div class="card-base z-10 px-9 py-6 relative w-full">
<Markdown class="mt-2">
<Content />
</Markdown>
</div>
</div>
</MainGridLayout>

注意事项

  • import 路径必须包含 .astro 扩展名
  • getEntry("spec", "talks") 的第一个参数是集合名,第二个是文件名(不含扩展名)
  • 使用 render 函数渲染 Markdown 内容

注意事项#

常见错误#

错误原因解决方案
LinkPreset.Talks is not defined枚举名不一致检查 config.ts 中的枚举名是否为 Talk
Cannot resolve import缺少 .astro 扩展名import 路径加上 .astro
Cannot read properties of undefinedlink-presets.ts 缺少映射添加 LinkPreset.Talk 的映射
导航栏不显示菜单页面开关为 false检查 siteConfig.pages.talks
页面 404路由文件不存在检查 src/pages/talks.astro
导航栏显示空白文字i18n 枚举键名大小写不匹配见下方 i18n 键名大小写问题

i18n 键名大小写问题#

问题描述:添加新功能后,导航栏菜单或页面标题显示为空白,没有文字。

根本原因src/i18n/i18nKey.ts 中枚举成员名与 link-presets.tstalks.astro 等文件中的引用名大小写不一致。TypeScript 枚举区分大小写,I18nKey.TalksI18nKey.talks 是不同的键,导致 i18n() 函数找不到翻译,返回空字符串。

错误示例(枚举成员是大写,引用是小写):

// ❌ i18nKey.ts — 枚举成员名是大写 Talk
export enum I18nKey {
Talks = "talks", // 大写 T
TalksDescription = "talksDescription",
}
// ❌ link-presets.ts — 引用的是小写 talks
name: i18n(I18nKey.talks), // 未定义的键 → 返回空字符串
// ❌ talks.astro — 同样引用小写
title={i18n(I18nKey.talks)}

正确写法(统一使用小写 camelCase):

// ✅ i18nKey.ts — 枚举成员名使用小写 camelCase(与其他键保持一致)
export enum I18nKey {
home = "home",
archive = "archive",
about = "about",
friends = "friends",
gallery = "gallery",
talks = "talks", // 小写 t
talksDescription = "talksDescription",
}
// ✅ link-presets.ts — 引用匹配
name: i18n(I18nKey.talks),
// ✅ talks.astro — 引用匹配
title={i18n(I18nKey.talks)}

同时别忘了添加翻译

// ✅ src/i18n/languages/zh_CN.ts
[Key.talks]: "说说",
[Key.talksDescription]: "我的日常动态和碎碎念",
// ✅ src/i18n/languages/en.ts
[Key.talks]: "Talks",
[Key.talksDescription]: "My daily updates and thoughts",

排查技巧:如果导航栏或页面标题显示空白,打开浏览器控制台查看是否有 i18n 相关警告,并检查 i18nKey.ts 中枚举成员名是否与所有引用处大小写一致。

完整检查清单#

  • src/content.config.ts 中定义了 spec 集合
  • src/content/spec/talks.md 文件已创建
  • src/types/config.ts 中添加了 Talk = 8
  • src/config/siteConfig.tstalks: true
  • src/config/navBarConfig.ts 中添加了菜单项
  • src/constants/link-presets.ts 中添加了预设映射
  • src/i18n/i18nKey.ts 中添加了 talkstalksDescription
  • src/pages/talks.astro 页面文件已创建

运行测试#

Terminal window
# 开发模式
pnpm dev
# 构建生产版本
pnpm build
# 预览生产版本
pnpm preview

快速参考模板#

如果你想添加一个新功能(如”笔记”),只需按以下步骤操作:

1. 创建内容文件#

文件src/content/spec/notes.md

---
title: 笔记
date: 2026-05-27
---
# 我的笔记
这里记录学习笔记和技术心得...

2. 添加枚举值#

文件src/types/config.ts

export enum LinkPreset {
// ...
Talk = 8,
Notes = 9, // ✅ 新增
}

3. 添加页面开关#

文件src/config/siteConfig.ts

pages: {
// ...
talks: true,
notes: true, // ✅ 新增
}

4. 添加导航栏配置#

文件src/config/navBarConfig.ts

children: [
...(siteConfig.pages.gallery ? [LinkPreset.Gallery] : []),
...(siteConfig.pages.talks ? [LinkPreset.Talk] : []),
...(siteConfig.pages.notes ? [LinkPreset.Notes] : []), // ✅ 新增
],

5. 添加预设映射#

文件src/constants/link-presets.ts

[LinkPreset.Notes]: {
name: i18n(I18nKey.notes),
url: "/notes/",
icon: "material-symbols:note",
},

6. 添加国际化键值#

文件src/i18n/i18nKey.ts

notes = "notes",
notesDescription = "notesDescription",

7. 创建页面路由#

文件src/pages/notes.astro

---
import MainGridLayout from "@/layouts/MainGridLayout.astro";
import { getEntry, render } from "astro:content";
import Markdown from "@components/common/Markdown.astro";
import I18nKey from "@/i18n/i18nKey";
import { i18n } from "@/i18n/translation";
const notesPost = await getEntry("spec", "notes");
if (!notesPost) {
throw new Error("Notes page content not found");
}
const { Content } = await render(notesPost);
---
<MainGridLayout
title={i18n(I18nKey.notes)}
description={i18n(I18nKey.notesDescription)}
>
<div class="flex w-full rounded-(--radius-large) overflow-hidden">
<div class="card-base z-10 px-9 py-6 relative w-full">
<Markdown class="mt-2">
<Content />
</Markdown>
</div>
</div>
</MainGridLayout>

总结#

添加自定义导航栏菜单项需要修改 8 个文件

序号文件修改内容
1src/content.config.ts定义内容集合(通常已存在)
2src/content/spec/xxx.md创建 Markdown 内容文件
3src/types/config.ts添加 LinkPreset 枚举值
4src/config/siteConfig.ts添加 pages.xxx 开关
5src/config/navBarConfig.ts添加导航栏菜单项
6src/constants/link-presets.ts添加预设映射
7src/i18n/i18nKey.ts添加国际化键值(注意小写 camelCase)
8src/pages/xxx.astro创建页面路由文件

别忘了:在 src/i18n/languages/ 下的所有语言文件中添加对应的翻译条目,否则导航栏和页面标题会显示空白。

按照以上步骤操作,即可成功添加自定义导航栏菜单项。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
自定义导航栏
https://f3f3.top/posts/custom-navbar/
作者
lyf
发布于
2026-05-27
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
lyf
Hello, I'm LyF.
公告
欢迎来到一飞的博客!。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
14
分类
5
标签
16
总字数
48,064
运行时长
0
最后活动
0 天前

文章目录

🤖 AI 助手

👋 你好!

我可以帮你解答关于这篇文章的问题