说起知识库这个东西,很多读者第一时间就会想到语雀或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、一点优化
如果你打算通过以上方式构建本地知识库,通常情况下你应该并不希望一直开个黑窗口。所以此时可以再做些小优化:
- 后台运行:通过
pm2
把docusaurus 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
习惯。
对于技术人来说,这简直不要太滋润。如果是企业内部服务,也是可以考虑这么搞,正真的降本增效…
如果啊,有机会,试试这种本地知识库服务吧