
1. 为什么我会把 AFFiNE 当作主力知识库来折腾第一次看到 AFFiNE 这个项目是在一个开源社区的周报里。标题很直白——“Notion 和 Miro 的融合体”当时我的第一反应是又是一个蹭热度的缝合怪。毕竟这几年打着“Notion 替代品”旗号的项目我试过不下十个大多数要么是文档编辑器套个壳要么是白板工具硬塞进文档功能用起来总有一种穿错鞋的别扭感。但 AFFiNE 不太一样它把文档Document和白板Edgeless做成了同一个页面的两种视图而不是两个割裂的模块。这个设计思路让我决定认真折腾一段时间。先说清楚这个项目到底是什么。AFFiNE 是一个开源的知识库工具核心能力可以拆成三块块编辑器负责结构化内容无限画布负责自由布局和视觉化思考本地优先的架构保证数据在自己手里。它想解决的问题很明确——现在很多团队的知识散落在文档工具、白板工具、任务管理工具里切换成本高信息孤岛严重。AFFiNE 试图用一个统一的页面模型把这些场景串起来。适合谁来用如果你是那种既需要写结构化文档、又习惯画思维导图或流程图的个人用户或者是一个小团队想找一个能自己部署、数据可控的协作知识库那这个项目值得花时间研究。如果你只是想要一个轻量的笔记软件那它可能有点重。我前后大概用了三个月从 Docker 自部署到日常写作、画架构图、整理项目资料基本把主要功能都跑了一遍。这篇文章不打算写成官方文档的中文翻译而是把我踩过的坑、想明白的设计逻辑、以及实际用下来的感受整理出来。你如果正在选知识库工具或者已经装了 AFFiNE 但不知道怎么用顺手下面的内容应该能帮你省不少时间。2. 核心设计思路拆解它凭什么敢说融合2.1 文档与白板的同源模型到底怎么回事很多工具做“文档白板”的思路是文档是一个模块白板是另一个模块两者之间通过链接或嵌入来关联。AFFiNE 的做法不同它底层用的是同一套块Block数据结构文档视图和白板视图只是同一份数据的两种渲染方式。这意味着你在文档里写的一段文字切到白板视图后它就是一个可以自由拖拽的卡片你在白板上画的一个形状切回文档视图它也能以某种形式呈现出来。这个设计的好处在于信息不会因为视图切换而丢失上下文。我举个例子我在写一篇技术方案时先在文档视图里把背景、目标、约束条件用文字列清楚然后直接按快捷键切到白板视图把这些要点拖成一张架构草图标注模块之间的依赖关系。整个过程不需要复制粘贴也不需要导出导入。改完草图再切回文档文字部分原封不动。这种流畅感是我在 Notion Miro 的组合里从来没有体验过的——在那边我得先想清楚哪些内容放文档、哪些放白板然后手动同步。当然这个模型也有代价。因为底层是同一套块结构白板上的自由绘制能力相比专业白板工具还是有差距。比如 Miro 里那种复杂的连线样式、丰富的模板库、精细的对齐辅助AFFiNE 目前还做不到那么细。但如果你不是专业设计师只是需要画个流程图、整理个思路那完全够用。2.2 本地优先架构为什么对知识库这么重要AFFiNE 的另一个核心卖点是本地优先Local-first。简单说就是你的数据首先存在本地云端同步是可选增强而不是必须依赖。这个选择对知识库类工具来说非常关键因为知识库往往承载的是一个人或一个团队最核心的思考沉淀一旦服务商出问题或者网络不通整个工作流就断了。我自己的部署方式是 Docker 自托管数据存在本地的 PostgreSQL 和对象存储里。日常使用中即使外网断了只要本地服务还在跑我照样能写文档、画图、搜索历史内容。同步到其他设备时AFFiNE 用的是 CRDT无冲突复制数据类型来做冲突合并这意味着多设备同时编辑同一篇文档时不会出现“后保存的覆盖先保存的”这种灾难。我实测过在台式机和笔记本上同时改同一段文字同步后两边的内容都保留了合并结果基本符合预期。不过这里要提醒一句本地优先不等于零配置。如果你选择自托管备份策略、存储容量、服务可用性都得自己操心。我见过有人把 AFFiNE 跑在一台旧笔记本上结果硬盘挂了数据全丢。所以自托管的前提是你得有一套靠谱的备份方案这个后面会细说。2.3 开源协议和社区生态的实际影响AFFiNE 用的是 MIT 协议这意味着你可以自由使用、修改、分发甚至拿来做商业产品。对于个人用户来说这个协议最大的好处是不用担心哪天突然被锁功能或者涨价。我经历过好几次用得好好的工具突然改成订阅制核心功能被砍到付费墙后面那种感觉非常糟糕。开源项目虽然也可能停止维护但至少你手里的代码是完整的社区里总有人能接手。社区生态方面AFFiNE 的插件系统还在早期阶段目前能用的第三方插件不多。但它的 API 是开放的我试过用它的 GraphQL 接口把文档内容同步到自己的静态博客生成器里流程不算复杂。如果你有开发能力可以基于它的 API 做很多定制化的事情比如自动生成周报、把白板内容导出成图片嵌入到其他系统里。3. 从零开始部署自托管 AFFiNE 的完整实操3.1 环境准备与依赖检查我选择的是 Docker Compose 部署方式这是目前最省心的方案。在开始之前你需要确认几件事一台有公网 IP 的服务器或者内网服务器加反向代理、Docker 和 Docker Compose 已安装、至少 2GB 内存和 10GB 磁盘空间。我一开始在一台 1GB 内存的轻量服务器上试结果 PostgreSQL 启动后频繁 OOM后来换成 2GB 才稳定。具体操作步骤# 更新系统包 sudo apt update sudo apt upgrade -y # 安装 Docker如果还没装 curl -fsSL https://get.docker.com | sh # 安装 Docker Compose sudo apt install docker-compose-plugin -y # 验证安装 docker --version docker compose version这里有个细节AFFiNE 的官方 Docker 镜像更新比较频繁建议在docker-compose.yml里指定具体版本号而不是用latest。我有一次自动更新后遇到数据库迁移失败回滚花了不少时间。指定版本号可以避免这种意外。3.2 Docker Compose 配置详解官方仓库里提供了docker-compose.yml模板但直接拿来用有几个地方需要调整。下面是我实际在用的配置关键参数都加了注释version: 3.8 services: affine: image: ghcr.io/toeverything/affine:0.16.0 # 指定版本避免自动更新出问题 container_name: affine restart: unless-stopped ports: - 3010:3010 # 默认端口如果冲突可以改左边 volumes: - ./data:/app/data # 持久化数据目录 - ./config:/app/config # 配置文件目录 environment: - AFFINE_SERVER_HOSTyour-domain.com # 换成你的域名或IP - AFFINE_SERVER_PORT3010 - AFFINE_DB_URLpostgresql://affine:passwordpostgres:5432/affine - AFFINE_REDIS_URLredis://redis:6379 - AFFINE_ENABLE_LOCAL_EMAILtrue # 本地邮箱登录适合个人使用 depends_on: postgres: condition: service_healthy redis: condition: service_started postgres: image: postgres:16-alpine container_name: affine-postgres restart: unless-stopped environment: - POSTGRES_USERaffine - POSTGRES_PASSWORDpassword # 务必改成强密码 - POSTGRES_DBaffine volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U affine] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: affine-redis restart: unless-stopped volumes: - ./redis-data:/data几个容易踩坑的地方第一AFFINE_SERVER_HOST必须填你实际访问的域名或 IP否则登录后跳转会出错。第二PostgreSQL 的密码不要用默认的我见过有人直接暴露在公网上被扫到数据被清空。第三如果你用 Nginx 做反向代理记得把 WebSocket 的升级头配置好否则白板协作会断连。3.3 首次启动与初始化设置配置写好后执行docker compose up -d然后等大概 30 秒让数据库初始化完成。用docker compose logs -f affine查看启动日志看到 “Server started” 之类的字样就说明成功了。浏览器访问http://你的IP:3010会进入注册页面。第一个注册的账号会自动成为管理员。这里建议用真实邮箱注册虽然后续可以改但初始账号的权限最高丢了会比较麻烦。注册完成后进入设置页面我建议先做三件事开启双因素认证如果你打算暴露在公网、配置备份路径、调整默认语言为中文。AFFiNE 的界面翻译完成度还不错但有些专业术语还是英文更准确这个看个人习惯。3.4 数据备份与恢复的实操方案自托管最大的风险就是数据丢失所以我单独把备份拎出来说。我的方案是每天凌晨 3 点用pg_dump导出数据库同时用rsync同步数据目录到另一台机器。具体脚本#!/bin/bash # backup-affine.sh BACKUP_DIR/backup/affine DATE$(date %Y%m%d) # 导出数据库 docker exec affine-postgres pg_dump -U affine affine $BACKUP_DIR/db-$DATE.sql # 同步数据目录 rsync -avz /path/to/affine/data/ $BACKUP_DIR/data-$DATE/ # 删除7天前的备份 find $BACKUP_DIR -name db-*.sql -mtime 7 -delete find $BACKUP_DIR -name data-* -type d -mtime 7 -exec rm -rf {} \;恢复的时候先停掉 AFFiNE 容器把数据库导入再把数据目录覆盖回去重启即可。我实测过一次恢复流程从备份到服务可用大概花了 15 分钟。这里有个经验定期演练恢复流程比备份本身更重要。我见过太多人备份文件存了一堆真出事的时候发现恢复步骤记不清了。4. 日常使用中的核心功能实操4.1 文档编辑器的块操作技巧AFFiNE 的文档编辑器基于块结构每个段落、标题、列表、代码块都是一个独立的块。这种设计的好处是拖拽重组非常方便。你可以直接按住块左侧的拖拽手柄把它拖到任意位置甚至拖到另一个页面里。我整理资料时经常用这个功能先把所有素材堆在一个页面里然后按主题拖到不同的子页面。快捷键方面有几个我几乎每天都会用的/唤出块类型菜单CtrlShift方向键快速移动块CtrlAlt数字切换标题级别。还有一个隐藏技巧选中多个块后按CtrlG可以把它们打包成一个可折叠的分组这在写长文档时特别有用可以把参考材料折叠起来保持主内容清爽。代码块的支持也值得一提。它内置了语法高亮支持的语言挺全而且可以一键复制。我写技术文档时经常嵌入代码片段AFFiNE 的代码块渲染效果比 Notion 更接近专业编辑器的观感。不过目前还不支持代码块内的自动补全这个期待后续版本。4.2 白板视图的实用场景与操作细节白板视图是我用得最多的功能之一。按CtrlShiftE可以在文档和白板之间切换同一份内容会以不同的布局呈现。白板上的基本操作和主流白板工具类似双击空白处创建便签拖拽便签边缘调整大小用连接线工具画箭头。但有几个细节做得比较贴心便签可以直接输入 Markdown写完后自动渲染成格式化的文本支持从文档视图拖拽块到白板文字内容会变成便签卡片画布可以无限扩展我试过放几百个节点缩放和拖拽依然流畅我常用的一个场景是项目复盘先在文档视图里列出时间线和关键事件然后切到白板把这些事件拖成一张流程图用不同颜色标注成功和失败节点最后再切回文档补充文字总结。整个过程在一个页面里完成不需要切换应用。不过白板的导出功能目前比较基础只能导出 PNG 和 SVG。如果你需要导出成可编辑的格式得用 API 自己处理。我试过用 Puppeteer 截取白板区域生成图片效果还行但复杂画布会有性能问题。4.3 多设备同步与协作的实际体验AFFiNE 的同步机制基于 CRDT我实测下来在局域网内几乎无感公网环境下大概有 1-2 秒的延迟。多设备同时编辑同一篇文档时冲突合并的结果基本符合直觉——两边新增的内容都会保留修改同一段文字时会以最后同步的版本为准但不会丢失任何一方的输入。协作方面你可以邀请其他用户加入工作区权限分为管理员、编辑者、评论者、查看者四种。我试过三个人同时在一张白板上操作各自的光标和选区会实时显示没有出现卡顿或错乱。不过目前还没有评论和提及功能团队沟通主要靠白板上的便签和文档里的文字说明。如果你的团队习惯用评论来讨论可能需要适应一下。5. 常见问题与排查技巧实录5.1 部署阶段的典型报错与解决我在部署过程中遇到过几个典型问题整理成表格方便对照问题现象可能原因解决方法容器启动后立即退出数据库连接失败检查AFFINE_DB_URL中的密码和主机名是否正确页面能打开但登录后白屏WebSocket 未配置Nginx 反向代理需添加Upgrade和Connection头上传图片失败存储目录权限不足确保./data目录对容器内用户可写同步延迟特别高Redis 未正常工作检查 Redis 容器日志确认没有内存溢出数据库迁移报错版本跨度过大逐版本升级不要直接从旧版跳到最新版其中 WebSocket 的问题最隐蔽因为页面能正常加载只是协作功能失效。排查方法是打开浏览器开发者工具看 Network 面板里有没有 WebSocket 连接失败的红色记录。如果有检查反向代理配置。5.2 使用过程中的性能优化建议AFFiNE 在文档块数量超过一定规模后编辑会开始出现卡顿。我实测下来单个页面超过 500 个块时输入延迟明显增加。优化方法有几个把大页面拆分成多个子页面用链接关联关闭不需要的实时协作功能定期清理历史版本。另外如果你用的是机械硬盘换成 SSD 对数据库查询速度提升非常明显。白板视图的性能瓶颈主要在节点数量。我试过在一张画布上放 1000 个以上的便签缩放时帧率会掉到 30 以下。建议把复杂白板拆成多个页面用链接跳转。如果必须放在一起可以先把部分节点折叠成分组减少渲染压力。5.3 数据迁移与导入导出的坑从 Notion 迁移到 AFFiNE 是我做过最折腾的事情之一。Notion 的导出格式是 Markdown CSV但它的数据库结构在 AFFiNE 里没有直接对应。我的做法是先把 Notion 页面导出为 Markdown然后用脚本把 CSV 里的表格数据转成 AFFiNE 的表格块。这个过程需要写一些代码没有现成的工具。AFFiNE 自己的导出功能支持 Markdown、PDF 和 HTML。导出 Markdown 时白板内容会以图片形式嵌入文字部分保留格式。如果你需要保留白板的可编辑性目前只能导出为 AFFiNE 自己的格式这意味着迁移到其他工具会比较困难。这是选择任何知识库工具都要考虑的问题数据可迁移性。我的建议是定期导出 Markdown 存档即使格式有损失至少文字内容不会丢。6. 我实际用下来的一些体会AFFiNE 最让我满意的地方是它把“写”和“画”真正打通了。以前我用 Notion 写文档、用 Miro 画图两边的内容经常对不上改了一边忘了同步另一边。现在在一个页面里切换视图就行信息始终是一致的。这种一致性对知识管理来说非常重要因为思考和表达本来就是交织在一起的硬要分成两个工具反而增加了认知负担。但也要客观说AFFiNE 目前还不是一个成熟的商业产品。它的移动端体验比较粗糙插件生态几乎空白某些高级功能比如数据库视图、公式计算还在开发中。如果你需要这些功能可能还得再等等。但如果你看重数据自主权、喜欢开源社区的氛围、并且愿意花一点时间折腾部署那它已经足够日常使用了。最后分享一个我常用的技巧把 AFFiNE 的 API 和自己的自动化脚本结合起来。比如我写了一个脚本每天定时抓取 RSS 订阅的内容自动创建到 AFFiNE 的收件箱页面里然后我在白板上对这些素材进行分类和关联。这样知识库就不是一个静态的仓库而是一个持续流动的信息处理管道。这个玩法需要一些开发基础但一旦跑通效率提升非常明显。