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 之类的工具进行部署。

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