Skip to content

VitePress 系列教程:自动生成侧边栏 #7

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

自动侧边栏

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

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

js
// 文件根目录
const DIR_PATH = path.resolve()
// 白名单,过滤不是文章的文件和文件夹
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 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]}`),
            })
        } else {
            // 获取名字
            const name = path.basename(params[file])
            // 排除非 md 文件
            const suffix = path.extname(params[file])
            if (suffix !== '.md') {
                continue
            }
            res.push({
                text: name,
                link: `${pathname}/${name}`,
            })
        }
    }
    return res
}

使用

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 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]}`),
            })
        } else {
            // 获取名字
            const name = path.basename(params[file])
            // 排除非 md 文件
            const suffix = path.extname(params[file])
            if (suffix !== '.md') {
                continue
            }
            res.push({
                text: name,
                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;

部署

运行 npm run docs:build 就可以将内容打包为静态内容,这个项目可以在 gitee pages 、github pages 进行托管,也可以自己购买服务器使用 nginx 之类的工具进行部署,甚至可以用 oss、cos 之类的工具进行部署。

最好是提前准备一个域名。