基础设置内容
自动生成侧边栏sidebar.mjs
js
// .vitepress/sidebar.mjs
import { readdirSync, statSync, existsSync, readFileSync } from 'node:fs'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import matter from 'gray-matter'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
function getFileFrontmatter(filePath) {
try {
if (!existsSync(filePath)) {
return { title: null, order: 999, sidebar: {} }
}
const content = readFileSync(filePath, 'utf-8')
const { data } = matter(content)
return {
title: data.title || null,
order: data.order !== undefined ? data.order : 999,
sidebar: data.sidebar || {}
}
} catch (error) {
console.warn(`读取文件 ${filePath} frontmatter 失败:`, error.message)
return { title: null, order: 999, sidebar: {} }
}
}
function hasValidContent(dirPath) {
if (!existsSync(dirPath)) {
return false
}
const entries = readdirSync(dirPath).filter(entry =>
!entry.startsWith('.') &&
!['.vitepress', 'public', 'node_modules', 'dist'].includes(entry)
)
if (entries.includes('index.md')) {
return true
}
const hasMdFiles = entries.some(entry =>
entry.endsWith('.md') && entry !== 'index.md'
)
if (hasMdFiles) {
return true
}
for (const entry of entries) {
const fullPath = join(dirPath, entry)
try {
const stats = statSync(fullPath)
if (stats.isDirectory() && hasValidContent(fullPath)) {
return true
}
} catch (error) {
// 忽略权限错误等,继续检查其他条目
}
}
return false
}
/**
* 递归处理目录结构,生成侧边栏菜单项
* @param {string} currentPath - 当前目录路径
* @param {string} baseUrl - 基础 URL 路径
* @param {number} level - 目录层级(用于控制折叠状态)
* @returns {Object[]} 生成的菜单项数组
*/
function processDirectory(currentPath, baseUrl, level = 0) {
const allItems = []
if (!existsSync(currentPath)) {
console.warn(`⚠️ 目录不存在: ${currentPath}`)
return allItems
}
let entries = []
try {
entries = readdirSync(currentPath).filter(entry =>
!entry.startsWith('.') &&
!['.vitepress', 'public', 'node_modules', 'dist'].includes(entry)
)
} catch (error) {
console.error(`❌ 读取目录失败: ${currentPath}`, error.message)
return allItems
}
const dirItems = []
const fileItems = []
entries.forEach(entry => {
const fullPath = join(currentPath, entry)
try {
const stats = statSync(fullPath)
if (stats.isDirectory()) {
// 检查目录是否有有效内容
if (hasValidContent(fullPath)) {
// 递归处理子目录
const subItems = processDirectory(fullPath, `${baseUrl}${entry}/`, level + 1)
// 读取目录下的 index.md 获取目录的排序信息
const indexMdPath = join(fullPath, 'index.md')
const dirFrontmatter = getFileFrontmatter(indexMdPath)
dirItems.push({
type: 'directory',
text: dirFrontmatter.title || entry,
collapsed: level >= 1,
items: subItems,
order: dirFrontmatter.order
})
}
} else if (stats.isFile() && entry.endsWith('.md') && entry !== 'index.md') {
// 处理 Markdown 文件(排除 index.md)
const fileName = entry.replace('.md', '')
const fileFrontmatter = getFileFrontmatter(fullPath)
fileItems.push({
type: 'file',
text: fileFrontmatter.title || fileName,
link: `${baseUrl}${fileName}`,
order: fileFrontmatter.order
})
}
} catch (error) {
console.warn(`处理路径 ${fullPath} 时出错:`, error.message)
}
})
// 分别对目录和文件按 order 排序
dirItems.sort((a, b) => (a.order || 999) - (b.order || 999))
fileItems.sort((a, b) => (a.order || 999) - (b.order || 999))
return [...dirItems, ...fileItems]
}
/**
* 生成侧边栏配置(支持 frontmatter 排序)
* @returns {Object} 侧边栏配置对象
*/
export function generateSidebar() {
const sidebar = {}
const docsRootPath = join(__dirname, '..')
// 定义顶级目录
const rootDirs = ['知识积累', '考试', '工作', '兴趣', '软件学习', '其他']
rootDirs.forEach(dir => {
const dirPath = join(docsRootPath, dir)
if (!existsSync(dirPath)) {
console.warn(`⚠️ 目录 "${dir}" 不存在,已跳过`)
return
}
try {
let sidebarItems = []
// 添加目录首页(如果存在 index.md)
const indexMdPath = join(dirPath, 'index.md')
if (existsSync(indexMdPath)) {
const indexFrontmatter = getFileFrontmatter(indexMdPath)
sidebarItems.push({
type: 'index',
text: indexFrontmatter.title || `${dir}首页`,
link: `/${dir}/`,
order: indexFrontmatter.order || 0
})
}
// 递归处理目录结构
const directoryItems = processDirectory(dirPath, `/${dir}/`)
sidebarItems = sidebarItems.concat(directoryItems)
// 按 order 字段排序整个侧边栏
sidebarItems.sort((a, b) => (a.order || 999) - (b.order || 999))
// 设置侧边栏配置
if (sidebarItems.length > 0) {
// 为整个顶级目录创建一个侧边栏分组
sidebar[`/${dir}/`] = [
{
text: dir,
collapsed: false, // 顶级目录默认展开
items: sidebarItems.map(item => {
const { type, order, ...rest } = item
return rest
})
}
]
// 关键:为每个子目录创建独立的侧边栏配置
// 遍历子目录,为它们创建独立的侧边栏
const subDirs = readdirSync(dirPath).filter(entry => {
const fullPath = join(dirPath, entry)
try {
return statSync(fullPath).isDirectory() && hasValidContent(fullPath)
} catch {
return false
}
})
subDirs.forEach(subDir => {
const subDirPath = join(dirPath, subDir)
const subIndexPath = join(subDirPath, 'index.md')
const subDirFrontmatter = getFileFrontmatter(subIndexPath)
// 创建子目录的侧边栏
const subSidebarItems = []
// 添加子目录首页
if (existsSync(subIndexPath)) {
subSidebarItems.push({
text: subDirFrontmatter.title || `${subDir}首页`,
link: `/${dir}/${subDir}/`,
order: subDirFrontmatter.order || 0
})
}
// 处理子目录的内容
const subDirItems = processDirectory(subDirPath, `/${dir}/${subDir}/`, 1)
subSidebarItems.push(...subDirItems)
// 排序
subSidebarItems.sort((a, b) => (a.order || 999) - (b.order || 999))
// 为子目录创建独立的侧边栏配置
sidebar[`/${dir}/${subDir}/`] = [
{
text: subDirFrontmatter.title || subDir,
collapsed: false,
items: subSidebarItems.map(item => {
const { type, order, ...rest } = item
return rest
})
}
]
})
} else {
console.warn(`⚠️ ${dir} 目录下没有找到有效内容`)
}
} catch (error) {
console.error(`❌ 处理目录 "${dir}" 时发生错误:`, error.message)
}
})
return sidebar
}
// 导出默认生成的侧边栏
export default generateSidebar()配置信息 config.mjs
json
import { defineConfig } from 'vitepress'
import nav from './nav.mjs'
import head from './head.mjs'
import { generateSidebar } from './sidebar.mjs'
// https://vitepress.dev/reference/site-config
export default defineConfig({
base: '/notes/',
title: "笔记站",
description: "我的笔记站",
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav,
head: head,
sidebar: generateSidebar(),
// 分页导航
docFooter: {
prev: '上一页',
next: '下一页'
},
// 编辑此页(如需开启请替换仓库地址)
editLink: {
text: '编辑此页面',
// 示例:替换为你的实际仓库路径
pattern: 'https://github.com/wuhaotdcq-s/notes/tree/main/docs/:path'
},
footer: {
message: '笔记站',
copyright: `版权所有 © 2025-${new Date().getFullYear()} 笔记站`
},
// 侧边栏相关
selectText: '选择笔记',
sidebarMenuLabel: '侧边栏',
toggleSidebar: '切换侧边栏',
outline: {
level: [1, 3],
label: "目录",
},
lastUpdated: {
text: '最近更新时间',
formatOptions: {
dateStyle: 'long',
timeStyle: 'medium'
}
},
darkModeSwitchLabel: "主题",
lightModeSwitchTitle: "切换到浅色模式",
darkModeSwitchTitle: "切换到深色模式",
search: {
provider: 'local',
options: {
translations: {
button: {
buttonText: '搜索文档',
buttonAriaLabel: '搜索文档'
},
modal: {
noResultsText: '无法找到相关结果',
resetButtonTitle: '清除查询条件',
footer: {
selectText: '选择',
navigateText: '切换',
closeText: '关闭'
},
},
},
},
},
markdown: {
lineNumbers: true,
container: {
tipLabel: '提示',
warningLabel: '注意',
dangerLabel: '警告',
infoLabel: '信息',
detailsLabel: '详情'
},
math: true,
codeTransformers: [],
config: (md) => {
// 可在此处添加 Markdown 插件
}
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' },
{ icon: 'bilibili', link: 'https://space.bilibili.com/66129848' },
{ icon: 'wechat', link: 'https://weixin.su/' }
]
}
})