基于Obsidian构建知识管理体系

Build a knowledge management system based on Obsidian

在2020年,我曾写过一篇文章如何构建自己的知识体系?,其中主要是对于日常技术积累的思考,偏向于方法论。

本篇文章会从工具和实际使用的角度出发,基于Obsidian探索个人知识管理的可能性,并尝试将其与Hexo博客结合起来。当然,这种工具偏好和实践具有强主观性,本文完全从我个人的实践和经历出发,没有最好的工具,只有最适合自己的。

前言

我曾用过很多的笔记软件,从印象笔记到Leanote,从Gist到Typora,以及各种乱七八糟的markdown编辑器。从中心化平台到自建后端,最后到纯文本加Dropbox同步内容,越来越觉得单纯从文本编辑的角度来说它们都没什么太大区别。至于一些第三方内容平台,像博客园、简书和知乎都写过,他们最大的问题是内容审查和逐渐娱乐化,越发觉得任何一个中心化的第三方内容平台都不好用,也不可靠。

在使用Obsidian之前,我一直使用的是Hexo部署博客,Typora编辑md文件,多设备使用Dropbox同步,所有的内容都通过博客作为载体管理和访问。但随着内容的增多,我迫切地希望有一种工具能够对知识库的内容进行分类组织和快速检索。

因为有时候想要快速定位到之前写的某些内容,Hexo博客的在线搜索对于单个页面字数非常多的情况下搜索性能很差,而且未发表的也搜索不到。本地使用Typora编辑单个文件比较好用,但是作为批量管理是不方便的,所以我基本上都是用SublimeText的目录搜索功能替代。

但缺点也显而易见,每次搜索都要打开SublimeText指定目录,还无法实时地预览md效果。并且基于纯文本的搜索形式,无法方便地对内容进行分类地筛选,就像博客的Tags或Categories那样。

由此,便进入了新的一个折腾工具的循环,就像拿着钉子去找一把新的锤子。

不得不说,现在的笔记工具市场真的日新月异,印象笔记早已经没落了,现在是双链笔记的天下。

目前比较流行的有Notion、Obsidian、Logseq,有了之前的经历之后,我对知识管理工具的选择确定了下面几个需求:

  1. 必须原生支持Markdown
  2. 不依赖任何云和帐号系统,数据自己完全控制
  3. 具有比较强大的搜索和分类能力
  4. 必须能够同时兼容Hexo博客的源文件及目录结构,不能相互干扰。博客依然是我进行知识管理的重要渠道。
  5. 功能可以自定义控制和扩展
  6. 方便迁移到其他的工具

综合评估下来,发现Obsidian比较符合我的需求,虽然官方在推自己的账户同步服务,但并不是必须的。我原本的目标也只是作为纯本地的工具使用,也可以自己基于S3的服务搞同步,这样数据更可控一些,后面会介绍方法。

而且它还有插件市场,目前上架的插件数量还不少,也可以自己基于js开发插件,能够比较方便的扩展功能。另外还有Dataview这种神级的插件,可以自己写代码控制知识库,对于程序员来说可以说是非常灵活了。

初识Obsidian

让GPT4先简短地介绍一下Obsidian的特点和优势:

[!ai]+ AI
Obsidian是一款能够以链接方式组织信息的知识管理工具。特点包括:基于Markdown的文本编辑,可选择本地或云端存储,文件通用性强;支持跨平台,随时随地编辑查看;除文本外支持音频视频等多种格式文件。优势是:观念管理和笔记整理一体化,秒级搜索功能,自由自在地建立知识链接网络,不断构建和优化自己的知识体系,还有图谱模式直观的展示思考逻辑和知识链接。

这里所说的知识链接网络,就是双链。一个笔记中可以直接引用另一个笔记中的内容,而无需把内容拷贝一份,这样对于相同的内容只需要维护一份,如果需要修订能够影响到所有引用他的地方。而且,这种方式也是为知识建立了一种联系,通过其中的一个点就能把相近方面的知识串起来。

理念很好,也很方便。

但特性越多,其实对于当前工具的依赖也就越多,我的想法是,工具可以辅助写作,但内容尽量不能有工具依赖,虽然我用Obsidian,但也以输出原生Markdown格式为最终目标。

再有,因为我用Hexo部署的博客是不支持双链这种方式的,所以我对于要公开发表的内容都只用通用的Markdown语法,其他的内容可以有限度的使用双链。

比如对于某项技术或者一些知识的全面梳理,就可以在单独的页面或者Canvas中双链引用其他文件里的笔记,把他们放到一起。

而且语法还非常简单:

  1. [[文件路径#标题]]是链接引用,点击就会跳转
  2. 在前面加!就是以内容的形式引用,直接把内容贴过来,并且可以编辑。

引用笔记中带有“反射”标题的内容

使用Canvas的白板功能组织内容,很直观:
Canvas中引用文章和笔记的内容

数据迁移

当决定要使用Obsidian之后,首先需要将之前的内容迁移过来。我之前的绝大部分文章和笔记都放在Hexo博客里面,提交到Github仓库之内,直接将博客的markdown源文件拷贝到Obsidian创建的Vault里就能够正常识别。

这就是基于Markdown管理的笔记文件的好处,只要目标工具支持Markdown格式就能够比较好的实现数据的迁移,而且之前博客中文件的组织方式还是比较规范的,所以只要把博客的源文件的路径当作Obsidian的Vault的根路径即可。

我的目标是使用Obsidian来管理博客和笔记在内的所有源文件,并且能够不影响博客内容的正常发布。而且还要能够精确的控制想要发表至博客的内容,而不是把整个仓库的内容全部发表。

Obsidian产生的其他文件,如.obsidian/.trash等内容与博客是隔离的,并且使用Obsidan管理的内容都放在_obsidian这个目录下。

.obsidian目录是存放软件设置、插件、主题等资料的目录。.trash是从Obsidian中删除文件之后,会移到该目录下,不会直接删除,多一层保险。

与Hexo博客整合

在之前的文章如何写好一篇技术文章#发布里讲到了,我是通过Dropbox同步源文件,在VPS上出发push命令,把md文件提交到一个单独的Git仓库,通过Github Actions触发构建的。

当接入Obsidian之后,我不想改变这个流程,所以我把博客和Obsidian的Vault变成同一个仓库。

  1. Dropbox + Github双重备份
  2. 把Obsidian和博客统一管理起来

要实现这点,需要让Hexo站点配置在生成时忽略.obsidian/.trash_obsidian下的所有文件。

修改站点的_config.yml

1
2
3
4
5
6
exclude:
- "_obsidian/*"
- ".obsidian/*"
- ".trash/*"
- "essay/tweets/*"
- "notes/ue/*"

这样,我在_obsidian下的所有笔记都不会被Hexo生成,起到一个私密效果。

然后把包含Obsidian数据的所有文件都提交到blog-md仓库即可:

无需对我原始的博客管理流程做任何修改,想要更新文章时,就Push这个仓库即可(甚至可以Github在线编辑),就能触发CI自动生成了。

插件推荐

使用了Obsdian有一年多了,装了几十个插件,基本都是我觉得比较好用,且能提升效率或扩展原始不支持功能的。

功能扩展

  • remotely-save:默认情况下不开通Obsidian账户是无法同步的,除非借助一些第三方工具,如Dropbox等。PC上很好,但在移动端上使用不便,remotely-save提供了可以支持S3存储桶来同步Vault内容,用对象存储的形式同步,很方便。(唯一的缺点是,多设备间同步.obsidian/plugins里面只能新增和修改,插件卸载了无法自动删除)
  • dataview:我觉得这是Obsdian里相当重要的插件,可以在笔记里嵌入js代码执行,这样就能实现笔记的动态效果,如计算日期、天气、按照一定规则筛选Obsidian中的笔记等等,都可以自己写代码实现。我也写了一些脚本,会在后面使用优化里介绍。
  • pangu:在Obsidain中支持看板,我用来进行任务归类管理
  • Natural Language Dates:支持以自然语言的形式插入日期,比如我要在笔记里插入当前时间,只需要输入@now,就能自动填充。
  • Full Calendar:扩展日历功能,可以在添加日程
  • drawio:可以在Obsidan中打开draw.io的画布
  • gist:支持通过代码块的形式插入gist代码
  • excalidraw:可以用来画手绘风格的流程图
  • Paste image rename:在插入图片后自动改名(如果图片存在本地的话)
  • Image auto upload Plugin:在插入图片时调用PicGo上传,并插入上传后的图片链接
  • Markdown Prettifier:可以自动格式化md文件,并且可以设置修改properties的值,比如编辑之后自动修改updated的时间,但如果在字数较多的文件中会卡(10w字量级)。
  • fuzzy chinese pinyin:可以用拼音的方式模糊搜索
  • text generator:在Obsdian中集成GPT进行文本生成

外观

  • Hider:可以隐藏Obsidian中的元素,哪些不顺眼都可以隐藏,我喜欢比较简约的形式,就隐藏了很多。
  • File Tree Alternative Plugin:目录结构的增强版,可以上下分栏显示。
  • Novel word count:可以在Ob自己的目录结构中展示当前目录下的总字数
  • front matter title:可以在Ob中用笔记中的title项替代文件名显示,在我从Blog迁移的文章中,都是时间戳命名的,用上这个工具可以很直观的看到文章名字。
  • hover editor:以悬浮窗口预览其他的笔记
  • homepage:支持打开软件时自动打开某个页面

我使用的主题是Things,比较简约,符合我的口味。

使用优化

日期

我希望在Obsdian中打开后,显示的homepage页面里,显示一些重要的信息。如当前日期、农历、以及周、月、年的进度。搜索后发现没有现成的轮子,就自己用js写了一个。效果如上图顶端的内容(移动端需安装Natural Language Dates插件)。

Hello,今天是2023年09月07日星期四,农历七月23日。本周还剩3天,九月还剩23天,2023年还剩115天。

使用dataviewjs嵌入js代码:

1
await dv.view("_obsidian/_data/_scripts/countdownView")

实际的脚本代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
CountdownView();

// 创建视图的主函数
async function CountdownView(){
var now_time = new Date();
var today_time = now_time.toLocaleString("en-US", {timeZone: "Asia/Shanghai"});
var day_number = moment(today_time).format('DD');
var month_number = moment(today_time).format('MMMM');
var year_number = moment(today_time).format('YYYY');

var formattedDate = moment(today_time).format('YYYY年MM月DD日dddd');
dv.el('span','Hello,今天是<span class="ob-special-font-style">'+formattedDate+'</span>,')

var today_local_month = now_time.toLocaleString('zh-u-ca-chinese', {month: "numeric"})
var today_local_day = now_time.toLocaleString('zh-u-ca-chinese', {day: "numeric"})
var today_local_day_num = Number(today_local_day.split('日')[0])

dv.el('span','农历<span class="ob-special-font-style">' + today_local_month + (today_local_day_num > 10 ? "" : "初") + today_local_day + '</span>。')

const nldates = app.plugins.getPlugin("nldates-obsidian")
var weekDiff,monthDiff,yearDiff;
if(nldates)
{
const today = nldates.parseDate("today").moment
const week = nldates.parseDate("next week").moment
const month = nldates.parseDate("next month").moment
const year = nldates.parseDate("next year").moment
weekDiff = week.diff(today,'days')
monthDiff = month.diff(today,'days')
const daysInMonth = today.daysInMonth()
yearDiff = year.diff(today,'days')
}
// momentjs
const NativeWeekDiff = Math.floor(moment(moment().startOf('week').add(1, "week")).diff(moment(), 'days'))
const NativeMonthDiff = moment(moment(moment().format("YYYY,MM")).add(1, "month").format("YYYY,MM[,1]")).diff(moment(), "days")
const NativeYearDiff = moment(moment(moment().format("YYYY")).add(1, "year").format("YYYY[,1,1]")).diff(moment(), "days")
if(NativeWeekDiff && NativeWeekDiff != NaN){ weekDiff = NativeWeekDiff; }
if(NativeMonthDiff && NativeMonthDiff != NaN){ monthDiff = NativeMonthDiff; }
if(NativeYearDiff && NativeYearDiff != NaN){ yearDiff = NativeYearDiff; }

dv.el('span','本周还剩<span class="ob-special-font-style">'+weekDiff+'</span>天,')
if(monthDiff==0){
dv.el('span','也是<span class="ob-special-font-style">'+month_number+'</span>的最后一天,')
}else{
dv.el('span',month_number+'还剩<span class="ob-special-font-style">'+monthDiff+'</span>天,')
}
dv.el('spwn',year_number+'年还剩<span class="ob-special-font-style">'+yearDiff+'</span>天。')
}

使用的CSS为(把日期的字体颜色改为蓝色,不使用的话则普通文本颜色相同):

1
2
3
4
5
.ob-special-font-style {
color: #4C8CE6;
font-size: 1em;
margin: 0 0.2em 0 0.2em;
}

天气

我还希望,打开之后能显示最近两天的天气。在obsidian的社区中发现了一个利用和风天气API的实现,能满足我的需求。我做了一些改动,效果如下:

dataviewjs的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
let setting = {};

//在和风天气中创建的 Api key
setting.key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
setting.city = "深圳";//城市名
setting.days = 2;//天气预报天数
setting.headerLevel = 0;//添加标题的等级
setting.addDesc = false;//是否添加描述
setting.onlyToday = false;//是否只在当天显示
setting.anotherCity = "";//添加另外一个城市

//脚本文件 weatherView.js 所在路径
await dv.view("_obsidian/_data/_scripts/weatherView",setting)

dv.view调用weatherView的js代码为:weatherView.js

Vault概览

我希望也能够实时地输出当前Obsidian仓库中的所有内容统计,如下:

共创建 383 篇文档、248 个标签、5 个看板,155 个待办、30 篇日记。180 篇博客文章,16 篇草稿。

同样也可以用dataviewjs来实现,代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let nofold = '!"_obsidian/_data/_Template"'
let allFile = dv.pages(nofold).file
let totalMd = "共创建 "+
allFile.length+" 篇文档"
let totalTag = allFile.etags.distinct().length+" 个标签"
let totalTask = allFile.tasks.length+" 个待办"

let kanbanFile = dv.pages('#看板 and !"_obsidian/_data"').file
let kanbanMddiarys = kanbanFile.length + " 个[[Homepage#看板|看板]]"

let diary_rule = '"_obsidian/_diary"'
let diaryFile = dv.pages(diary_rule).file
let totalMddiarys = diaryFile.length + " 篇[[ObHome#日记|日记]]"

let posts_rule = '"_posts"'
let postsFile = dv.pages(posts_rule).file
let draft_rule = '"_draft"'
let draftFile = dv.pages(draft_rule).file
let totalMdArticles = postsFile.length - 1 + " 篇[[BlogHome#已发布|博客文章]]," + draftFile.length+" 篇[[BlogHome#草稿|草稿]]。"

dv.paragraph(
totalMd+"、"+totalTag+"、"+ kanbanMddiarys + "," +totalTask +"、" + totalMddiarys + "。" + totalMdArticles
)

内容筛选

以及列出我博客中的所有非文章页面,并且可以跳转到网页中:

实现代码(dataview):

1
2
3
4
table WITHOUT ID link(file.link,file.link.title) AS "笔记",elink("https://imzlp.com/"+file.folder,substring(file.folder,6)) AS 链接,file.link.description as "描述"
from "notes" and !"notes/index"
sort file.size desc
limit 100

使用这种方式就能用代码的形式去遍历整个Ob中的内容,并且能够访问其中的metadata数据,进行筛选。

插件开发和优化

Obsidian提供了非常强大的插件机制,前面列举了我常用的一些插件。但是有些插件也不能100%让我用起来完全满意,所以可以对已有的插件进行定制化改造或者开发新的插件。

Obsidian的插件开发使用的是TypeScript,需要执行编译才能输出实际能用的插件。
如果对某个插件不满意,就可以找到Github的仓库,自己修改TypeScript的代码,然后生成新的插件。

file-tree-alternative插件为例:
默认的版本只能展示文件名,但我的Hexo文章的文件名都是时间戳命名的,看起来不直观。所以我基于原始仓库修改了一下默认的版本,使其能够读取文章里的title项展示。仓库详见:imzlp/ob-file-tree-alternative。下载release中的文件,替换插件的main.js即可。

官方版本与我修改后的版本对比图:

是不是就直观了很多呢,这也是开放插件生态的优势啊。

工具带来的改变

切换到Obsdian之后,对于我而言进行知识管理而言变成了一个不那么繁琐的事情。倒不是因为用了多少Ob独有的特性,而是它能把我常用的工具整合起来,并且按照我自己的需求定制。

  1. 可以全平台同步了
  2. 能够方便地检索内容了(文件管理、TAG,搜索等)
  3. 可以直接使用更复杂的工具了(看板、draw.io、excalidraw、Canvas等)
  4. 对于日程管理,可以更精确了,在Calendar里可以点击日期创建日记,并添加某个时间点的日程,这样打开Ob就能清楚地看到最近要做哪些事情。
  5. 博客管理更方便了,在Ob里能够快速导航到某个博客页面,也能够基于模板可以直接创建出空白的文章格式文件。并且移动到_posts目录下就能发表。
  6. 简化了操作步骤,把精力回归内容本身,让我更想去写东西了。

我的Obsidan主页面:

结语

知识管理最重要的是什么?内容!内容!还是内容!没有内容产出的工具折腾是没有意义的。
工欲善其事,必先利其器。利其器的目的是为了更好更快地去做事情,而不只是把工具弄好,就本末倒置了。

Obsdian确实能让我很方便地写内容,也很好用。但我认为更重要的是对待内容的态度,认真地写、积极地分享,发挥出它的最大价值,才是“知识管理”的根本。

全文完,若有不足之处请评论指正。

微信扫描二维码,关注我的公众号。

本文标题:基于Obsidian构建知识管理体系
文章作者:查利鹏
发布时间:2023年09月07日 16时19分
更新时间:2023年09月26日 10时42分
本文字数:本文一共有5.4k字
原始链接:https://imzlp.com/posts/30911/
许可协议: CC BY-NC-SA 4.0
文章禁止全文转载,摘要转发请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!