VitePress侧边栏自动生成实现
前言
在使用VitePress搭建博客时,一个常见的需求是根据文章自动生成侧边栏导航。本文将详细介绍如何实现一个支持多语言、按年月分组的侧边栏自动生成工具。
实现目标
- 自动扫描文章目录,提取文章信息
- 支持中英文两种语言
- 按年月对文章进行分组展示
- 自动生成侧边栏配置数据
核心实现
1. 数据结构设计
首先定义所需的接口类型:
typescript
// generate-sidebar.ts
// 文章元数据接口
interface ArticleMeta {
title: string
date: string
tags?: string[]
excerpt?: string
url: string
lang: string
}
// 侧边栏项目接口
interface SidebarItem {
text: string
link?: string
items?: SidebarItem[]
collapsed?: boolean
}
// 侧边栏配置接口
interface SidebarConfig {
[path: string]: SidebarItem[]
}
2. 文章扫描与解析
使用fast-glob
库扫描文章目录,并通过gray-matter
解析文章frontmatter:
typescript
// generate-sidebar.ts
const files = await glob(['zh/**/*.md', 'en/**/*.md'], {
cwd: srcDir,
ignore: [
'node_modules/**',
'.vitepress/**'
],
absolute: false,
onlyFiles: true
})
// 解析文章信息
for (const file of files) {
const content = fs.readFileSync(fullPath, 'utf-8')
const { data: frontmatter } = matter(content)
// 跳过非文章页面
if (frontmatter.noneArticle) continue
const article: ArticleMeta = {
title: frontmatter.title || path.basename(file, '.md'),
date: frontmatter.date || new Date().toISOString(),
tags: frontmatter.tags || [],
excerpt: frontmatter.excerpt,
url: `/${file.replace(/index\.md$/, '')}`,
lang: file.startsWith('zh/') ? 'zh' : 'en'
}
}
TIP
对于不需要出现在侧边栏中的特殊页面,可以在frontmatter中添加noneArticle: true
标记。 例如:
markdown
---
layout: Articles
title: 文章
noneArticle: true
---
3. 文章分组处理
按语言、年、月对文章进行分组:
typescript
// generate-sidebar.ts
const articles: {
[lang: string]: {
dates: { [year: string]: { [month: string]: ArticleMeta[] } }
}
} = {
zh: { dates: {} },
en: { dates: {} }
}
// 将文章按日期分组
const date = new Date(article.date)
const year = date.getFullYear().toString()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
if (!articles[lang].dates[year]) {
articles[lang].dates[year] = {}
}
if (!articles[lang].dates[year][month]) {
articles[lang].dates[year][month] = []
}
articles[lang].dates[year][month].push(article)
4. 生成侧边栏配置
将分组后的文章数据转换为VitePress侧边栏配置格式:
typescript
// generate-sidebar.ts
const MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
const sidebarConfig: SidebarConfig = {}
for (const lang of ['zh', 'en']) {
const langPrefix = `/${lang}/articles/`
const isZh = lang === 'zh'
sidebarConfig[langPrefix] = Object.entries(articles[lang].dates)
.sort(([a], [b]) => b.localeCompare(a)) // 年份降序
.map(([year, months]) => ({
text: isZh ? `${year}年` : year,
items: Object.entries(months)
.sort(([a], [b]) => b.localeCompare(a)) // 月份降序
.map(([month, posts]) => ({
text: isZh
? `${month}月 (${posts.length})`
: `${MONTH_NAMES[parseInt(month) - 1]} (${posts.length})`,
collapsed: false,
items: posts
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
.map(post => ({
text: post.title,
link: post.url
}))
}))
}))
}
5. 配置文件生成
将生成的配置数据写入JSON文件:
typescript
// generate-sidebar.ts
const dataDir = path.resolve(__dirname, '../data')
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true })
}
fs.writeFileSync(
path.join(dataDir, 'sidebar-data.json'),
JSON.stringify(sidebarConfig, null, 2)
)
使用方式
- 在VitePress配置文件中加载生成的侧边栏数据:
typescript
// config.mts
function loadSidebarData() {
const sidebarPath = path.resolve(__dirname, '../theme/data/sidebar-data.json')
try {
if (fs.existsSync(sidebarPath)) {
const sidebarData = JSON.parse(fs.readFileSync(sidebarPath, 'utf-8'))
return sidebarData
}
} catch (error) {
console.warn('Failed to load sidebar data:', error)
}
return {}
}
// 在配置中使用
export const config = {
themeConfig: {
sidebar: {
'/zh/': loadSidebarData()['/zh/articles/'] || [],
'/en/': loadSidebarData()['/en/articles/'] || []
}
}
}
- 在构建流程中执行生成脚本:
json
{
"scripts": {
"generate:sidebar": "tsx .vitepress/theme/scripts/generate-sidebar.ts",
"dev": "npm run generate:sidebar && vitepress dev",
"build": "npm run generate:sidebar && vitepress build"
}
}
最终效果
生成的侧边栏数据结构如下:
json
// sidebar-data.json
{
"/zh/articles/": [
{
"text": "2025年",
"items": [
{
"text": "01月 (3)",
"collapsed": false,
"items": [
{
"text": "SwiftData结合CKSyncEngine实现iCloud同步",
"link": "/zh/Implementing-iCloud-Sync-by-Combining-SwiftData-with-CKSyncEngine/"
}
]
}
]
}
],
"/en/articles/": [
{
"text": "2025",
"items": [
{
"text": "January (3)",
"collapsed": false,
"items": [
{
"text": "Implementing iCloud Sync by Combining SwiftData with CKSyncEngine",
"link": "/en/Implementing-iCloud-Sync-by-Combining-SwiftData-with-CKSyncEngine/"
}
]
}
]
}
]
}
总结
通过这个工具,我们实现了:
- 自动扫描并解析文章内容
- 支持多语言分组展示
- 按时间顺序组织文章
- 自动生成VitePress侧边栏配置
这大大简化了博客维护工作,使我们可以专注于内容创作。