自动加载导航
介绍
因为比较懒,不想每次整理导航都需要去修改config
文件,所以写了一个方法自动读取文件夹,然后自动生成导航。 先放一下我自己的config.ts
的配置:
ts
import { defineConfig } from 'vitepress'
import markdownItKatex from 'markdown-it-katex';
import { navConfig } from '../config/nav'
import { sidebarConfig } from '../config/sidebar'
import { customElements } from '../config/katex'
// console.log(process.env.NODE_ENV)
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "煊赟知镜",
description: "\“煊\”,光明、温暖之意,象征着家庭中充满爱与关怀的氛围,每一个家庭成员在这里都能感受到如阳光般的温暖照耀。“赟”,美好之意,寓意着家庭生活的丰富多彩、美好幸福。",
base: '/',
outDir: '../dist/',
srcDir: 'src',
vite: {
server: {
host: '0.0.0.0',
port: 5173,
cors: true
}
},
head: [
['meta', { name: 'theme-color', content: '#3eaf7c' }],
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }],
['meta', { name: 'msapplication-TileColor', content: '#000000' }],
['meta', { name: 'viewport', content: 'width=device-width,initial-scale=1,user-scalable=no' }],
['meta', { name: 'msapplication-TileImage', content: '/static/images/favicon.ico' }],
['link', { rel: 'apple-touch-icon', href: '/static/images/favicon.ico' }],
['link', { rel: 'mask-icon', href: '/static/images/favicon.ico', color: '#3eaf7c' }],
['link', { rel: 'icon', href: '/static/images/favicon.ico' }],
],
themeConfig: {
siteTitle: '煊赟知镜',
logo: '/static/images/logo.png',
nav: navConfig,
sidebar: sidebarConfig,
search: {
provider: 'local'
},
outline: {
// level: 'deep',
level: [2, 3],
label: '页面导航',
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/trexwb' }
],
docFooter: {
prev: '上一篇',
next: '下一篇'
},
footer: {
copyright: '以上言论仅代表个人观点'
}
},
sitemap: {
hostname: 'https://xuanyun.wang/'
},
markdown: {
config: (md) => {
md.use(markdownItKatex)
},
lineNumbers: true,
math: true,
// defaultHighlightLang: 'php',
image: {
// 默认禁用图片懒加载
lazyLoading: true
}
},
vue: {
template: {
compilerOptions: {
isCustomElement: (tag) => customElements.includes(tag)
}
}
}
})
重要的代码就是nav: navConfig
和sidebar: sidebarConfig
,关于数学函数katex
这个部分我就不做解释了,网上很多。
- nav.data.ts 遍历需要显示在顶部导航的目录,返回成vitepress需要的格式
ts
import { es } from 'element-plus/es/locales.mjs';
import fs from 'fs';
import path from 'path';
interface FileOrDir {
text?: string;
link?: string;
items?: FileOrDir[];
}
const filterDirs = [
'demo',
'public',
'文档',
'编程物语',
'课外知识'
];
const allowedDirs = [
'笔记',
'收藏'
]
function buildJsonFromNextLevel(directoryPath: string, depth: number = 0): FileOrDir[] | FileOrDir {
const absoluteDirectoryPath = path.join(__dirname, directoryPath);
const subDirectories = fs.readdirSync(absoluteDirectoryPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory() && !filterDirs.includes(dirent.name) && allowedDirs.includes(dirent.name))
.map(dirent => dirent.name);
// 处理当前目录下的文件
const filesInCurrentDir = fs.readdirSync(absoluteDirectoryPath)
.filter(item => !fs.statSync(path.join(absoluteDirectoryPath, item)).isDirectory())
.map(item => {
const filePath = path.join(directoryPath, item);
const fileNameWithoutExtension = path.parse(item).name;
const textName = fileNameWithoutExtension === 'index' ? path.basename(path.dirname(filePath)) + '-导读' : fileNameWithoutExtension;
const relativePath = path.relative(process.cwd(), filePath).replace('../src/', '');
return { text: textName, link: `/${relativePath}` };
});
let result: FileOrDir[] = [];
// 如果当前目录下只有一个文件,且不是根目录,则直接返回该文件
if (filesInCurrentDir.length === 1 && depth > 0) {
if (subDirectories.length === 0) {
// 当前目录下只有一个文件
result.push({ text: path.basename(directoryPath), link: filesInCurrentDir[0].link });
} else {
// 当前目录下有其他目录时
result.push({
text: path.basename(directoryPath),
items: [
{ text: filesInCurrentDir[0].text, link: filesInCurrentDir[0].link }
]
});
}
} else if (depth > 0) {
// 当前目录下有多个文件或没有文件
const tmpRus: FileOrDir = { text: path.basename(directoryPath), items: [] };
for (const file of filesInCurrentDir) {
(tmpRus.items as FileOrDir[]).push(file);
}
// 如果 items 数组为空,则移除 items 属性
if (tmpRus.items && tmpRus.items.length === 0) {
delete tmpRus.items;
}
result.push(tmpRus);
}
// 下级目录
for (const subDirName of subDirectories) {
const subDirPath = path.join(directoryPath, subDirName);
const subDirData = buildJsonFromNextLevel(subDirPath, depth + 1);
const subNameWithoutExtension = path.parse(subDirName).name;
const items = subDirData as FileOrDir[];
if(items.length > 0) {
if(items.length === 1){
result.push(...items)
} else {
result.push({
text: subNameWithoutExtension,
items: items
})
}
}
}
return result;
}
const directoryPath = '../src/';
// 构建目录树
export const navData = buildJsonFromNextLevel(directoryPath);
// console.log('navData:', JSON.stringify(navData));
// export const navData = [
// {
// text: '笔记',
// items: [
// { text: '日常', link: '/notes/daily/index' },
// {
// text: '踩坑记',
// items: [
// { text: 'Debug清单', link: '/notes/debug/index' },
// { text: '踩坑记录', link: '/notes/logs/index' },
// ]
// },
// ]
// },
// {
// text: '工具',
// items: [
// { text: '软件推荐', link: '/tools/share/index' },
// { text: '在线工具', link: '/tools/online/index' },
// { text: 'IDE配置', link: '/tools/ide/index' },
// {
// text: '密钥破解',
// items: [
// { text: '相关密钥', link: '/tools/secret/index' },
// { text: '破解工具', link: '/tools/decryption/index' },
// ]
// },
// {
// text: '开源代码',
// items: [
// { text: 'Git代码', link: '/tools/resources/index' },
// ]
// },
// {
// text: 'AI',
// items: [
// { text: '豆包', link: 'https://www.doubao.com/'},
// { text: '通义千问', link: 'https://tongyi.aliyun.com'},
// ]
// },
// ]
// },
// ]
// export default {
// navData: navData
// }
export default {
navData: navData,
load() {
return {
navData: navData,
}
}
}
- sidebar.data.ts 遍历需要根据目录显示在侧边导航的目录,返回成vitepress需要的格式
ts
import fs from 'fs';
import path from 'path';
interface FileOrDir {
text?: string;
collapsed?: boolean;
link?: string;
items?: FileOrDir[];
}
const filterDirs = [
'demo',
'public'
];
function buildJsonFromNextLevel(directoryPath: string, buildMenu: Record<string, FileOrDir[]> = {}, depth: string = '/'): FileOrDir[] | FileOrDir {
const absoluteDirectoryPath = path.join(__dirname, directoryPath);
const subDirectories = fs.readdirSync(absoluteDirectoryPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory() && !filterDirs.includes(dirent.name))
.map(dirent => dirent.name);
// 处理当前目录下的文件
const filesInCurrentDir = fs.readdirSync(absoluteDirectoryPath)
.filter(item => !fs.statSync(path.join(absoluteDirectoryPath, item)).isDirectory())
.map(item => {
const filePath = path.join(directoryPath, item);
const fileNameWithoutExtension = path.parse(item).name;
const textName = fileNameWithoutExtension === 'index' ? path.basename(path.dirname(filePath)) + '-导读' : fileNameWithoutExtension;
const relativePath = path.relative(process.cwd(), filePath).replace('../src/', '');
return { text: textName, link: `/${relativePath}` };
});
let result: FileOrDir[] = [];
// 如果当前目录下只有一个文件,且不是根目录,则直接返回该文件
if (filesInCurrentDir.length === 1) {
if (subDirectories.length === 0) {
// 当前目录下只有一个文件
result.push({ text: path.basename(directoryPath), link: filesInCurrentDir[0].link });
} else {
// 当前目录下有其他目录时
result.push({
text: path.basename(directoryPath),
collapsed: false,
items: [
{ text: filesInCurrentDir[0].text, link: filesInCurrentDir[0].link }
]
});
}
} else {
// 当前目录下有多个文件或没有文件
const tmpRus: FileOrDir = { text: path.basename(directoryPath), collapsed: false, items: [] };
for (const file of filesInCurrentDir) {
(tmpRus.items as FileOrDir[]).push(file);
}
// 如果 items 数组为空,则移除 items 属性
if (tmpRus.items && tmpRus.items.length === 0) {
delete tmpRus.items;
}
result.push(tmpRus);
}
if(!buildMenu[depth]){
buildMenu[depth] = result;
} else {
buildMenu[depth].push(...result);
}
for (const subDirName of subDirectories) {
const subDirPath = path.join(directoryPath, subDirName);
const subDirData = buildJsonFromNextLevel(subDirPath, buildMenu, `${depth}${subDirName}/`);
const items = subDirData as FileOrDir[];
if(items.length > 0) {
if(!buildMenu[`${depth}${subDirName}/`]){
buildMenu[`${depth}${subDirName}/`] = items;
} else {
buildMenu[`${depth}${subDirName}/`].push(...items);
}
}
}
return buildMenu;
}
const directoryPath = '../src/';
// 构建目录树
export const sidebarData = buildJsonFromNextLevel(directoryPath);
// console.log('sidebarData:', JSON.stringify(sidebarData));
// export const sidebarData = {
// '/notes/daily/': [
// {
// text: '随笔',
// collapsed: false,
// items: [
// { text: '随笔(待整理)', link: '/notes/daily/index' },
// { text: 'demo', link: '/notes/daily/demo' },
// ]
// }, {
// text: '2024',
// collapsed: false,
// items: [
// { text: 'x x x x', link: '/notes/daily/2024/xxxx' },
// ]
// },
// ],
// '/tools/share/': [
// {
// text: '网络工具',
// collapsed: false,
// items: [
// { text: 'PDF双页一键切割单页', link: '/tools/share/pdf2page' },
// ]
// }, {
// text: '系统相关',
// collapsed: false,
// items: [
// { text: 'x x x x', link: '/tools/share/xxxx' },
// ]
// }, {
// text: '设计相关',
// collapsed: false,
// items: [
// { text: 'x x x x', link: '/tools/share/xxxx' },
// ]
// }, {
// text: '文字处理',
// collapsed: false,
// items: [
// { text: 'x x x x', link: '/tools/share/xxxx' },
// ]
// }, {
// text: '媒体处理',
// collapsed: false,
// items: [
// { text: 'x x x x', link: '/tools/share/xxxx' },
// ]
// },
// ],
// }
export default {
sidebarData: sidebarData,
load() {
return {
sidebarData: sidebarData,
}
}
}