Skip to content

自动生成侧边栏

在做博客的时候,制作博客首页的时候的需要获取文章详情数据构建目录页,有时候还需要对文章进行分类、排序等其他操作。这一节我们来学习如果获取文章的元数据,构建自己想要实现的页面。

自动侧边栏

使用VitePress构建博客的时候,侧边栏需要自己手动配置。当文章与目录一多,就特别麻烦。但是可以自己使用Node.js编写代码,实现自动生成侧边栏。

1、先定义需要过滤的白名单,把 index.md.vitepress 等之类的文件或者目录给过滤掉。

js
// 文件根目录
// const DIR_PATH = path.resolve()
const DIR_PATH = path.resolve() + "/docs/"

// 白名单,过滤不是文章的文件和文件夹
const WHITE_LIST = ['index.md', '.vitepress', 'node_modules', '.idea', 'assets']

2、遍历目录下的文件与文件夹,并且过滤白名单

js
import path from 'node:path'
import fs from 'node:fs'

// 文件根目录
// const DIR_PATH = path.resolve()
const DIR_PATH = path.resolve() + "/docs/"

// 白名单,过滤不是文章的文件和文件夹
const WHITE_LIST = ['index.md', '.vitepress', 'node_modules', '.idea', 'assets']


// 判断是否是文件夹
const isDirectory = (path) => fs.lstatSync(path).isDirectory()

// 取差值
const intersections = (arr1, arr2) => Array.from(new Set(arr1.filter((item) => !new Set(arr2).has(item))))


export const set_sidebar = (pathname) => {
    // 获取 pathname 的路径
    const dirPath = path.join(DIR_PATH, pathname)
    // 读取 pathname 下的所有文件或者文件夹
    const files = fs.readdirSync(dirPath)
    // 过滤掉
    const items = intersections(files, WHITE_LIST)
    console.log(items)
    return {}
}

3、递归遍历目录,生成侧边栏菜单

js
// 把方法导出直接使用
function getList(params, path1, pathname) {
    // 存放结果
    const res = []
    // 开始遍历params
    for (let file in params) {
        // 拼接目录
        const dir = path.join(path1, params[file])
        // 判断是否是文件夹
        const isDir = isDirectory(dir)
        if (isDir) {
            // 如果是文件夹,读取之后作为下一次递归参数
            const files = fs.readdirSync(dir)
            res.push({
                text: params[file],
                collapsible: true,
                // items: getList(files, dir, `${pathname}/${params[file]}`),
                // 在路径前添加 / 否则上下文链接无法正确显示
                items: getList(files, dir, `/${pathname}/${params[file]}`), 
            })
        } else {
            // 获取名字
            const name = path.basename(params[file])
            // 排除非 md 文件
            const suffix = path.extname(params[file])
            if (suffix !== '.md') {
                continue
            }
            // 去除文件名前序号和.md后缀名
            let reName = name.replace(/\.md$/, '');	
            reName = reName.replace(/[0-9]*-/, '');	
            res.push({
                // text: name,
                // link: `/${pathname}/${name}`,
                // 去除文件名前序号和.md后缀名
                text: reName,	
                // 在路径前添加 / 否则上下文链接无法正确显示
                link: `/${pathname}/${name}`,	
            })
        }
    }
    return res
}

使用 config.mjs

js
import {defineConfig} from 'vitepress'
import {set_sidebar} from './set_sidebar.mjs'

export default defineConfig({
    themeConfig: {
        sidebar: {'/nuxt3': set_sidebar('nuxt3')},  
    },
})
完整代码
js
import path from 'node:path'
import fs from 'node:fs'

// 文件根目录
// const DIR_PATH = path.resolve()
const DIR_PATH = path.resolve() + "/docs/"

// 白名单,过滤不是文章的文件和文件夹
const WHITE_LIST = ['index.md', '.vitepress', 'node_modules', '.idea', 'assets']


// 判断是否是文件夹
const isDirectory = (path) => fs.lstatSync(path).isDirectory()

// 取差值
const intersections = (arr1, arr2) => Array.from(new Set(arr1.filter((item) => !new Set(arr2).has(item))))

// 把方法导出直接使用
function getList(params, path1, pathname) {
    // 存放结果
    const res = []
    // 开始遍历params
    for (let file in params) {
        // 拼接目录
        const dir = path.join(path1, params[file])
        // 判断是否是文件夹
        const isDir = isDirectory(dir)
        if (isDir) {
            // 如果是文件夹,读取之后作为下一次递归参数
            const files = fs.readdirSync(dir)
            res.push({
                text: params[file],
                collapsible: true,
                // items: getList(files, dir, `${pathname}/${params[file]}`),
                // 在路径前添加 / 否则上下文链接无法正确显示
                items: getList(files, dir, `/${pathname}/${params[file]}`), 
            })
        } else {
            // 获取名字
            const name = path.basename(params[file])
            // 排除非 md 文件
            const suffix = path.extname(params[file])
            if (suffix !== '.md') {
                continue
            }
            // 去除文件名前序号和.md后缀名
            let reName = name.replace(/\.md$/, '');	
            reName = reName.replace(/[0-9]*-/, '');	
            res.push({
                // text: name,
                // link: `/${pathname}/${name}`,
                // 去除文件名前序号和.md后缀名
                text: reName,	
                // 在路径前添加 / 否则上下文链接无法正确显示
                link: `/${pathname}/${name}`,	
            })
        }
    }
    return res
}

export const set_sidebar = (pathname) => {
    // 获取pathname的路径
    const dirPath = path.join(DIR_PATH, pathname)
    // 读取pathname下的所有文件或者文件夹
    const files = fs.readdirSync(dirPath)
    // 过滤掉
    const items = intersections(files, WHITE_LIST)
    // getList 函数后面会讲到
    return getList(items, dirPath, pathname)
}

获取文章元数据

在做文章目录页的时候,需要获取一些文章里面的数据,这时候就可以使用 VitePress 默认提供的 createContentLoader 方法。

js
// posts.data.js
import {createContentLoader} from 'vitepress';

const pages = createContentLoader('/posts/*.md', {
    includeSrc: false,
    render: false,
    excerpt: false,
    transform(rawData) {
        return rawData.sort((a, b) => {
            return +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date);
        });
    },
});

export default pages;