cover-img
avatar

托码特人

分享科技与人文

一个关注互联网的技术博客

本地知识库搭建

说起知识库这个东西,很多读者第一时间就会想到语雀notion这种公有云平台。

当然,如果只是为了图省事,亦或需要在任何地方都能编辑或访问,那公有云方案确实是比较合适的选择。
但是,如果仅仅是为了记录自己的日常、并且有较高的私密性要求,那本地知识库搭建你绝对有必要了解一下。

先说结论吧:

  • 优势
    • 绝对私密且没有账户密码管理负担;🍐
    • 不用担心平台跑路或者不维护了(实际上很多公有云平台都不保险,特别是国内的互联网环境下);🏃
    • 自由创作,写啥都可以,不用担心说了不恰当的话被封号;🙊
    • 自由定制(需要一些技术能力),需要啥功能自己自足,不需要办卡充会员;🙅
  • 劣势
    • 只能自己电脑或私服访问,是否需要 Everywhere 取决你自己;
    • 有一定技术门槛,但非常有限;

缘起

大概 10 年前吧,我第一次接触Markdonw这种玩意,然后瞬间被以这种形式记录工作、日常的姿势深深吸引。包括后来搭建独立站,内容的载体都是一个个.md文件,乃至你现在看到的这篇文章,原始内容仍然是…后来由于工作和生活需要,我基于 vuepress 1.x 在本地搭建了一个知识库,通过Github托管内容。

这期间,由于 Vue3 的发布,很多基于 V2 版本的类库都出现了问题,比如当nodejs升级了,就发现原来本地的服务就跪了。再后来,官方搞了个 vitepress ,然后就没有然后了(vuepress 被正式晾在了一边,尽管现在有社区维护,但已经不好再作为第一选择了)!

由于本人技术出身,所以对于这类问题基本上都能处理,然而对于框架的新特性或者想要扩展一些额外的功能,就略显尴尬了…特别是到了今年,问题尤为明显。直到前两天,因为 tailwindcss库导致和vuepress某个依赖发生冲突然后直接启动不了了,所以就怒换了:Docusaurus

因为实在不想再继续为这老古董消耗时间和精力去折腾了,docusaurus 提供知识库基础能力的同时,还额外提供了比其他方案更多的优势

知识库套件

在换docusaurus之前,我想过升级成vitepress,因为在这种事情上,越少折腾越好。但后来发现:把 vuepress 迁移到 vitepress 并不容易

同样是体力活,索性一把换到位。于是最终选择了React解决方案

Docusaurus 指南

抱歉,没有这趴!毕竟官方文档已经写的很详细了,这里确实没必要再做搬运工…😏

从 Vuepress 的迁移

1、通过 create-docusaurus 生成目标站点

nlx create-docusaurus@latest MarsNotes classic --typescript

注意:这里的nlx是全局antfu ni的命令,没有装ni的可以根据自己需要换其他的,比如:yarn create xxx效果是一样的。当然这一步官方有详细的指北

额外的依赖,建议都安装一下

ni @docusaurus/plugin-ideal-image @easyops-cn/docusaurus-search-local docusaurus-plugin-image-zoom

插件的配置和作用,对应到 github 的 readme 都有,请自行取用…

2、配置说明

把 vuepress 迁移到 docusaurus 比我想像中要容易的多,因为原来的知识库目录层级较多,有好几个大的分类。在 vuepress 托管时,愣是写了一大托node-fs操作代码,用来生成目录树结构:

import fs from "node:fs";
import path from "node:path";

/**
 * 返回边栏数据结构体
 * @param group 组标题
 * @param collapsable 是否折叠
 * @param children 文章标题集
 */
export const SidebarGroupItem = (group: string, collapsable: boolean = true, children: any[] = []) => {
  return {
    title: group,
    collapsable: collapsable,
    children: children,
  };
};

/**
 * 根据本地文件层级及文件名,动态生成树状边栏数据
 * @param category 分类名称
 * @param catePath 分类文件根目录
 * @param group 分组
 * @returns
 */
export const genSiderbarGroups = (category: string, catePath: string, group: any[] = []) => {
  if (!fs.existsSync(catePath)) {
    console.error("catePath not exists:", catePath);
    return null;
  }

  return fs.readdirSync(catePath).reduce(
    (bars, file) => {
      const currentFile = path.join(catePath, file);
      const urlPath = currentFile.split(category + "/")[1];

      if (fs.statSync(currentFile).isFile() && path.parse(currentFile).ext === ".md") {
        const file = urlPath.slice(0, -3).replace(/README$/g, "");
        path.parse(currentFile).dir.endsWith(category) ? bars[0].children.push(file) : group.push(file);
      }

      if (fs.statSync(currentFile).isDirectory()) {
        const pathName = path.parse(currentFile).name;
        const curGroup = SidebarGroupItem(pathName);

        urlPath.split("/").findIndex((item) => item === pathName) > 0 ? group.push(curGroup) : bars.push(curGroup);
        genSiderbarGroups(category, currentFile, curGroup.children);
      }

      return bars;
    },
    [SidebarGroupItem("概述", false)],
  );
};

vuepress 1.x 动态目录树构建

import path from "node:path";
import { SidebarConfig4Multiple } from "vuepress/config";
import { genSiderbarGroups } from "./shared";

const Categories = ["cate1", "cate2", "cate3", "cate4", "cate5"];

export const Sidebar4ZH: SidebarConfig4Multiple = Categories.reduce((bars, category) => {
  const categoryRootPath = path.resolve(__dirname, `../../../${category}`);
  bars[`/${category}/`] = genSiderbarGroups(category, categoryRootPath);
  return bars;
}, {});

但发现docusaurus 对动态目录树是原生支持的,它的sidebars.ts里有一段注释:”By default, Docusaurus generates a sidebar from the docs folder structure”

这就不要太爽好吧,所以在 docusaurus 配置知识库的目录树就异常简单了

import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";

const sidebars: SidebarsConfig = {
  cate1Sidebar: [{ type: "autogenerated", dirName: "cate1" }],
  cate2Sidebar: [{ type: "autogenerated", dirName: "cate2" }],
  cate3Sidebar: [{ type: "autogenerated", dirName: "cate3" }],
};

export default sidebars;

其中,cate1、cate2…这些,对应 docs 目录中的其他大类目录名称
个别细节部分,比如知识库首页就需要自己根据需要定制了,入口:src/pages/index.tsx

如此,就基本完成迁移后的配置了!

3、一点优化

如果你打算通过以上方式构建本地知识库,通常情况下你应该并不希望一直开个黑窗口。所以此时可以再做些小优化:

  • 后台运行:通过pm2docusaurus start放在后台;
  • 反向代理:选个你喜欢的域名,改本地 hosts,使其支持通过这个域名访问到 docusaurus 服务,这一步可以通过nginx搞定

pm2 的应用启动配置

{
  "name": "my-wiki",
  "script": "pnpm run build && pnpm run serve",
  "cwd": "./my-wiki"
}

注意这里启动命令是run serve而非run start。是因为通过构建后运行,docusaurus 会做服务端渲染。不过有个问题就是当你再新建或改动旧 MD 文件后,站点并不会更新,当然这个很好理解啦。所以如果需要时时更新,主要需要通过run start启动(docusaurus start)

上 Git

这点非常重要!

原因也很简单,因为你并不希望可能某天由于电脑进水了,然后资料丢失吧~
所以,选个你喜欢的 git 托管服务,创建一个private仓库,把本地知识库保存在云端就完事了。

通过 git 托管,还有一个好处,就是你能看到自己的创作记录,当然这个实际效果取决于自己的commit习惯。

对于技术人来说,这简直不要太滋润。如果是企业内部服务,也是可以考虑这么搞,正真的降本增效…
如果啊,有机会,试试这种本地知识库服务吧

赞赏

声明: 本文内容由托码斯创作整理,由于知识水平和时效性问题,行文可能存在差错,欢迎留言交流。读者若需转载,请保留出处,谢谢!