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

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