AFFiNE — 文档和白板共用同一棵 block 树的开源知识库
是什么
AFFiNE 是一个开源的知识库工具,把”写文档”和”画白板”做成同一个东西。日常类比:像一张能折叠的纸——平铺时是文档,立体翻折后是画板,但你写在上面的字始终是同一份字,不会因为折叠而被复制成两份。
你新建一个 doc,写下”今天的会议笔记”。在顶部点一下切换按钮,刚才那行字立刻出现在一块自由画板里,可以拖动、可以画箭头连到其他文字。再切回去,文档模式里那行字也跟着改了——因为它们根本是同一段数据。
这套机制让 AFFiNE 同时是 Notion 的替代品(文档)和 Miro 的替代品(白板),但它的目标不是”功能合并”,而是”数据合并”。
为什么重要
不理解 AFFiNE 的设计思路,你就无法解释这些事:
- 为什么有些团队愿意花时间自托管一个笔记工具,而不是直接付钱用 Notion
- 为什么”local-first”(本地优先)这个词最近几年突然流行——和 CRDT 这种数学工具有什么关系
- 为什么”开源”和”商业化”可以同时存在(AGPL + commercial 双 license)
- 为什么”文档”和”白板”两类工具市面上一直分开,但底层其实可以统一
核心要点
AFFiNE 把三件事糅在一起:
-
block 是原子单位,不是文件:传统工具里”文档”和”白板”是两种文件,各自有 schema。AFFiNE 把一段文字、一张图、一个标题都视为 block,不论显示在文档里还是白板里都是同一份 block。类比:乐高积木,可以摆成房子也可以摆成车,但积木块本身不变。
-
CRDT 处理多人同步:你和同事各自离线编辑同一个 doc,再次联网时不需要”谁先谁后”的判断——数学上保证两份编辑能自动合并不冲突。类比:两个画家在两张透明纸上画同一幅画,叠起来时颜料不会互相冲掉。
-
本地优先(local-first):数据先存在你的设备(IndexedDB 或 SQLite),云端只是”可选的中转站”。断网照常工作,自托管就是把中转站换成你自己的服务器。类比:把云盘当备份,主文件还在自己电脑里。
实践案例
案例 1:一行代码切换文档/白板模式
togglePrimaryMode() { this.setPrimaryMode( this.getPrimaryMode() === 'edgeless' ? 'page' : 'edgeless' );}逐部分解释:
edgeless是白板模式,page是文档模式- 整个切换只是改一个字段,不复制 block、不迁移数据
- 因为底层是同一棵 block 树,UI 层只是换种渲染方式,类似”同一份 JSON 用列表渲染还是表格渲染”
案例 2:本地存储不依赖云
// 简化版:写一段文字ydoc.getText('content').insert(0, '今天的会议笔记');
// Yjs 把这次操作编码成一段二进制 updateconst update = encodeStateAsUpdate(ydoc);
// 本地 IndexedDB 直接存await indexedDB.put('doc-123', update);整个过程没碰服务器。下次联网时把累积的 update 推给后端,后端只是”把二进制按时间顺序追加”,不解析、不合并。合并发生在客户端。
案例 3:自托管启动
git clone https://github.com/toeverything/AFFiNEcd AFFiNE
# 起依赖容器docker compose -f .docker/dev/compose.yaml up -d postgres redis
# 装依赖、跑 migration、起后端yarn installyarn workspace @affine/server prisma migrate deployyarn workspace @affine/server start
# 另起 frontendyarn workspace @affine/web dev后端是 NestJS + Postgres + Redis 三件套。整套起来后浏览器打开 localhost:8080 就能注册账号用。这是和 Notion 最直接的差异——Notion 没有任何”自己装一份”的路径。
踩过的坑
-
AGPL 传染性:任何 fork 部署给外部用户都触发”必须开源衍生改动”。企业用之前要算清楚是用商业 license 还是接受开源义务,否则法务踩雷。
-
学习曲线陡:要看懂源码需要会 TypeScript + React + Yjs + NestJS + Prisma + monorepo 至少 5 个生态,新人 onboarding 通常 2 周起。比读普通 Notion-clone 项目复杂得多。
-
CRDT 存储会膨胀:append-only 的 update 流写多了会几 MB 起步,需要后台 merge job 定期合并。merge job 挂了或来不及,存储会无限增长——生产部署一定要监控这张表。
-
生态比 Notion 弱:模板市场、第三方集成、API 文档完整度都还在追赶。想”无缝从 Notion 迁过来 + 保留所有连接器”的诉求做不到。
适用 vs 不适用场景
适用:
- 想要”数据归我 + 文档和白板都要 + 团队协作 + 自托管选项”的团队
- 学习 Yjs CRDT、local-first 架构、TypeScript monorepo 的实战范例
- 离线工作场景(出差 / 网差环境 / 内网部署)
不适用:
- 要求”开箱即用 + 不维护后端”——Notion 仍然是最省心的选择
- 重度依赖 Notion 第三方集成(Zapier、Notion API 接的工具链)
- 移动端为主的用户——AFFiNE 的 mobile 端 2026 年还在迭代
- 闭源商业产品想 vendor 它的代码——AGPL 不允许
历史小故事(可跳过)
- 2022 年:toeverything 团队开源 AFFiNE 第一版,定位是 “Notion + Miro + Airtable 的开源融合”。当时的 block 模型还很初级,doc 和 whiteboard 是分开实现的。
- 2023 年:架构大改,把编辑器引擎抽出独立 repo(BlockSuite),doc 和 whiteboard 开始共享同一棵 block 树——这是”hyper-merged”的真正诞生。
- 2024 年:用 Yjs 替换早期同步方案,正式确立 local-first 路线。AFFiNE Cloud 上线,定下 AGPL + commercial 双 license 模式。
- 2025 年:移动端 app(iOS / Android)发布,BlockSuite 适配触屏交互。
- 2026 年初:v0.26 系列,stars 破 6.8 万,11000+ commits、550+ releases。仍是高频活跃项目,每周多次合并到 canary 分支。
学到什么
-
“数据格式统一”比”UI 统一”更深刻:很多工具号称”all-in-one”,但底层各功能各存一份;AFFiNE 在 block 颗粒度上真正合并,UI 只是视图。
-
server 可以很笨:传统协作工具的 server 解析 + 合并 + 广播;AFFiNE 的 server 只做存储 + 转发,所有合并逻辑放在客户端的 CRDT 引擎里。这是 local-first 的精髓。
-
license 是产品决策:选 AGPL 既能开源吸引贡献者,又能用 commercial license 卖给企业——但代价是限制了”想白嫖部署”的用户群。
-
DDD 模块化在前端:70+ 个 module 用 Service/Entity/Scope 切分,比 Redux + reducer 文件夹清爽得多——值得借鉴到任何中大型 React 项目。
延伸阅读
- 官方文档:docs.affine.pro —— Local-first philosophy 那节最值得读
- Yjs 入门:Yjs documentation —— 理解 CRDT 二进制 update 的工作原理
- local-first 宣言:Local-First Software —— Ink & Switch 实验室的原始论文,AFFiNE 设计哲学的源头
- BlockSuite 独立 repo:toeverything/blocksuite —— 想做 block-based 编辑器但不想要整套 backend,单用这个包
- yjs —— AFFiNE 的同步引擎
- crdt-json —— CRDT 数学基础
关联
- yjs —— AFFiNE 把 Yjs 用到极致的产品级范例
- crdt-json —— 文档协作 CRDT 的论文基础
- excalidraw —— 同样是开源白板,但只做白板不做文档
- prosemirror —— 另一种富文本编辑器架构,与 BlockSuite 思路对照
- nestjs —— AFFiNE 后端用的 Node.js 框架
- react —— 前端 UI 库
- paxos —— 协作系统的另一类一致性方案,与 CRDT 形成对比
反向链接
- collabora-online —— Collabora Online — 浏览器里直接编辑 Office 文档的开源后端
- crdt-json —— CRDT JSON — 协同编辑 JSON 数据结构
- excalidraw —— Excalidraw — 手绘风协作白板
- nestjs —— NestJS — 把 Angular 思想搬到 Node.js 后端的企业级框架
- paxos —— Paxos — 分布式共识算法
- prosemirror —— ProseMirror — schema 先定 DOM 后服从的富文本编辑器框架
- react —— React UI 组件库
- tldraw —— tldraw — 把白板做成可嵌入的 SDK
- yjs —— Yjs — 让任何编辑器都能接的协同编辑内核