ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AFFiNE开源替代Notion:文档白板表格融合与本地优先部署实战

AFFiNE开源替代Notion:文档白板表格融合与本地优先部署实战 1. 为什么我要认真聊聊 AFFiNE 这个项目第一次听说 AFFiNE 是在一个开源社区的闲聊帖里有人甩了一句“Notion 的下一代开源替代品”底下跟了几十条讨论。我当时的第一反应是又一个蹭 Notion 热度的项目罢了。毕竟这几年打着“Notion 替代”旗号的产品没有一百也有八十大部分要么是套壳编辑器要么功能残缺得厉害用两天就弃了。但 AFFiNE 不太一样。我花了一个周末把它从源码编译跑起来又用了大概两周时间把日常的笔记、文档、简单的项目看板都迁移过去试了一遍结论是这东西确实值得认真对待。它不是简单的“开源版 Notion”而是在文档和数据库的融合方式上走出了一条自己的路——把文档、白板、表格三种形态揉在同一个页面空间里这个设计思路本身就很有意思。这篇文章适合几类人看一是对 Notion 这类一体化工作空间有需求、但又在意数据主权和长期可用性的朋友二是想研究现代协同文档编辑器架构的开发者三是单纯想找一个能自己部署、不依赖第三方服务的知识管理工具的人。我会从整体设计思路、核心技术点、实际部署和使用的完整流程、以及踩过的坑这几个维度展开尽量把我知道的都倒出来。需要提前说明的是AFFiNE 目前仍在快速迭代中我写的内容基于我实际使用的版本后续版本可能有变化。另外我不会把它吹成完美无缺的东西该说的问题我也会直说。2. 整体设计思路文档、白板、表格为什么要揉在一起2.1 传统笔记工具的“形态割裂”问题用过 Notion 的人应该都有体会写文档是一个页面做看板是另一个页面画流程图又得跳到白板工具里去。虽然 Notion 后来加了白板功能但本质上还是几个独立模块拼在一起数据之间没有真正的融合。你在文档里写了一段需求描述想把它直接拖到白板上做头脑风暴或者变成表格里的一行任务操作起来是割裂的。我自己的痛点特别明显。做项目规划的时候我习惯先在白板上画架构草图然后在文档里写详细说明最后在表格里排任务和时间线。这三个东西在传统工具里是三份独立的数据改了一处另外两处不会自动同步。时间一长白板上的图和文档里的描述就对不上了维护成本很高。AFFiNE 的设计思路就是冲着这个问题去的。它的核心概念叫“Edgeless”和“Page”两种模式的无缝切换。同一个内容块你可以在文档视图里把它当成一段文字切到白板视图里它就变成一个可以自由拖拽的卡片。底层数据是同一份只是渲染方式不同。这个设计听起来简单但实现起来涉及不少架构上的取舍。2.2 底层数据模型的选择逻辑AFFiNE 底层用的是 CRDTConflict-free Replicated Data Type无冲突复制数据类型来做协同编辑的数据同步。具体来说它早期用的是 Yjs 这套库后来自己做了不少封装和优化。为什么选 CRDT 而不是 OTOperational Transformation这是个很关键的架构决策。OT 的思路是所有操作经过服务器排序后再分发保证一致性。优点是成熟、很多协同编辑器都在用缺点是强依赖中心服务器离线场景处理起来很麻烦。CRDT 的思路是每个操作本身携带足够的信息任何顺序应用都能收敛到同一个状态。优点是天然支持离线编辑和点对点同步缺点是数据体积会膨胀对垃圾回收要求高。AFFiNE 选择 CRDT我认为核心原因是它想支持“本地优先”Local-first的使用方式。你可以完全离线在本地编辑等联网后再同步不会出现冲突丢失。这对那些经常在没网环境下工作、或者对数据隐私特别在意的人来说是很实在的价值。当然代价就是同步的数据包会大一些初次加载和长期使用后的存储优化需要额外处理。2.3 块级编辑器的实现取舍AFFiNE 的编辑器是基于块Block的这点和 Notion 一样。但它的块模型比 Notion 更“扁平”一些。Notion 的块是严格树形嵌套的一个块只能有一个父块。AFFiNE 在某些场景下允许块有多个引用关系这就为白板模式下的自由布局提供了基础。具体来说AFFiNE 用了一个叫“BlockSuite”的编辑器框架这是他们自己开源的。BlockSuite 把文档模型、编辑器视图、协同层做了比较清晰的分层。文档模型层定义块的结构和属性编辑器视图层负责渲染和交互协同层处理 CRDT 同步。这种分层的好处是同一份文档模型可以对应多种视图——文档视图、白板视图、表格视图都是不同的视图层实现共享同一个模型层。这个架构的优点是扩展性好想加新的视图类型相对容易。缺点是初期开发复杂度高各层之间的边界需要仔细设计不然容易出现视图和模型不同步的 bug。我在使用过程中确实遇到过几次切换视图后内容显示异常的情况刷新一下就好了说明这块还在打磨中。3. 核心技术点拆解几个值得关注的实现细节3.1 BlockSuite 编辑器框架的分层设计BlockSuite 是 AFFiNE 团队开源出来的编辑器框架可以单独使用。它的分层大概是这样的Model 层定义块的数据结构包括块的类型、属性、子块关系等。这一层不关心怎么渲染只关心数据长什么样。View 层负责把 Model 渲染成用户能看到的界面。文档视图、白板视图、表格视图都是不同的 View 实现。Command 层定义各种操作命令比如插入块、删除块、修改属性、移动块等。所有对文档的修改都通过命令来执行方便做撤销重做和协同同步。Sync 层基于 CRDT 做多端同步处理冲突合并。这个分层的好处是职责清晰。比如我想加一个“思维导图视图”只需要写一个新的 View 层实现Model 和 Command 层基本不用动。坏处是抽象层次多了之后调试起来比较绕。有一次我遇到一个块在白板视图里位置不对的问题追了半天发现是 View 层的一个坐标转换计算有误跟 Model 层的数据没关系。3.2 CRDT 同步机制的实际表现CRDT 在实际使用中的表现我分几个场景来说单机离线编辑完全没问题所有操作都在本地完成响应速度很快。AFFiNE 会把操作记录存在本地的 IndexedDB 里等联网后再同步。多端同时编辑我试过两台电脑同时编辑同一个文档一台改文字一台拖白板上的卡片同步后两边内容都正确合并了没有出现丢失。但同步有延迟大概几秒到十几秒不等取决于网络状况和操作复杂度。长时间使用后的性能CRDT 的一个已知问题是操作历史会不断累积导致文档加载变慢。AFFiNE 有做快照和压缩机制但我用了一个月左右、文档里积累了几百个块之后确实感觉打开速度比刚开始慢了一些。后来我手动整理了一下把一些不再需要的编辑历史清理掉速度就恢复了。所以建议定期做一下文档整理别让一个文档无限膨胀。3.3 本地优先与云端同步的平衡AFFiNE 支持两种使用模式纯本地模式和云端同步模式。纯本地模式就是所有数据存在浏览器或本地客户端里不经过任何服务器。云端同步模式则需要自己部署一个 AFFiNE 的服务端或者用他们提供的托管服务。我两种都试过。纯本地模式适合个人使用数据完全在自己手里但多设备同步就没办法了。云端同步模式需要自己部署服务端我用 Docker 在一台小服务器上跑了一个配置不算复杂但要注意数据备份和版本升级的问题。这里有个细节值得说AFFiNE 的云端同步不是简单的“把本地数据上传到服务器”而是服务器也维护一份 CRDT 文档各端和服务器做双向同步。这意味着服务器本身也是一个“客户端”只是它不渲染界面。这种设计的好处是同步逻辑统一坏处是服务器需要一定的计算资源来处理 CRDT 合并。4. 从零开始部署 AFFiNE 的完整实操流程4.1 环境准备与依赖检查我选择用 Docker 部署服务端这是最省事的方式。先确认服务器上装了 Docker 和 Docker Compose。我用的是 Ubuntu 22.04Docker 版本 24.xCompose 版本 v2.x。这些基础环境如果还没装网上教程很多这里不展开。需要准备的资源一台能跑 Docker 的服务器配置不用太高1 核 2G 内存起步就够个人用一个域名可选但建议有方便 HTTPS 访问如果要用 HTTPS还需要准备证书可以用 Lets Encrypt 免费申请注意AFFiNE 的服务端对内存有一定要求如果文档多、协同频繁建议至少 2G 内存。我一开始用 1G 内存的机器跑文档多了之后偶尔会卡顿加到 2G 后就顺畅了。4.2 Docker 部署服务端的详细步骤AFFiNE 官方提供了 Docker 镜像部署命令大致如下。我把它拆成几个步骤来说明第一步创建数据目录和配置文件mkdir -p /opt/affine/data cd /opt/affine第二步写一个 docker-compose.yml 文件version: 3 services: affine: image: ghcr.io/toeverything/affine-graphql:stable container_name: affine restart: unless-stopped ports: - 3010:3010 volumes: - ./data:/root/.affine/storage environment: - AFFINE_SERVER_HOST你的域名或IP - AFFINE_SERVER_PORT3010 - AFFINE_SERVER_HTTPSfalse第三步启动服务docker compose up -d第四步检查日志确认启动成功docker compose logs -f看到类似“Server started”的日志就说明起来了。然后浏览器访问http://你的服务器IP:3010就能看到界面。提示如果要用 HTTPS建议在前面加一个 Nginx 反向代理把 443 端口的请求转发到 3010。Nginx 配置里记得加 WebSocket 支持因为 AFFiNE 的协同同步走的是 WebSocket。4.3 客户端配置与首次使用服务端跑起来后客户端可以用浏览器直接访问也可以下载桌面客户端。桌面客户端的好处是支持本地文件系统集成可以把附件存在本地而不是浏览器缓存里。首次使用时需要注册一个账号。如果是自己部署的服务端第一个注册的账号通常就是管理员。注册完成后可以创建一个工作区Workspace然后就可以开始建文档了。我建议第一次使用时先做几件事在设置里把语言改成中文如果界面默认是英文检查一下存储路径确认数据存在你期望的位置建一个测试文档试试文档、白板、表格三种视图的切换如果有多台设备试试同步是否正常4.4 数据备份与迁移的注意事项自己部署服务端数据备份是必须的。AFFiNE 的数据主要存在两个地方一个是 PostgreSQL 数据库存元数据和用户信息一个是文件存储目录存文档内容和附件。备份策略我建议数据库每天定时 dump 一次保留最近 7 天的备份文件存储目录用 rsync 定期同步到另一台机器或对象存储升级版本前一定要先备份因为数据库结构可能变化迁移的话把数据库 dump 和文件目录一起搬到新服务器按同样的方式部署然后把数据恢复进去就行。注意版本要一致跨大版本迁移可能会有兼容问题。5. 实际使用中遇到的坑与排查记录5.1 同步冲突与数据恢复我遇到过一次比较严重的问题两台设备同时编辑同一个文档其中一台设备在离线状态下改了很多内容另一台设备也在线改了不少等离线设备联网同步后发现部分内容出现了重复块。排查下来原因是离线设备在同步前做了本地快照压缩导致部分操作历史丢失同步时无法正确合并。解决办法是尽量不要在长时间离线后直接同步大量修改可以先导出本地内容同步后再手动合并。AFFiNE 有导出功能支持导出为 Markdown、PDF 等格式。我现在的习惯是如果要在离线状态下做大量修改先导出一份备份心里踏实。5.2 性能问题的排查思路前面提到过文档用久了会变慢。我总结了一个排查流程现象可能原因排查方法解决方式打开文档慢操作历史过多看文档块数量和编辑历史长度整理文档删除无用块同步延迟高网络问题或服务器负载高检查服务器 CPU 和内存使用率升级服务器配置或优化网络白板卡顿块数量过多或浏览器性能不足看白板上的块数量和浏览器内存占用拆分白板减少单页块数量搜索慢索引未更新或数据量大检查搜索索引状态重建索引或限制搜索范围我实测下来单个文档的块数量控制在 500 以内体验最好。超过 1000 块之后编辑和同步都会明显变慢。所以建议把大文档拆成多个小文档用链接关联起来。5.3 常见问题速查问题Docker 容器启动后无法访问检查端口是否被占用防火墙是否放行以及AFFINE_SERVER_HOST是否配置正确。问题注册账号后无法登录检查数据库连接是否正常看服务端日志有没有报错。有时候是数据库迁移没完成导致的。问题附件上传失败检查存储目录的权限确保 Docker 容器有写入权限。另外注意文件大小限制默认可能有限制可以在配置里调整。问题多设备同步后内容不一致先刷新页面如果还不一致检查各设备的网络连接。实在不行导出内容后重新导入。问题升级后数据丢失升级前一定要备份。如果已经丢了看备份文件是否完整按备份恢复流程操作。提示AFFiNE 的社区比较活跃遇到问题可以去他们的 GitHub Discussions 搜一下很多坑别人已经踩过了。提问的时候附上服务端日志和复现步骤得到回复的概率会高很多。6. 我对 AFFiNE 的一些个人判断用了这段时间我对 AFFiNE 的整体评价是方向对完成度在快速提升但还没到可以无脑推荐给所有人的程度。如果你是对数据主权有要求、愿意花点时间折腾部署、并且能接受一定不稳定性的用户AFFiNE 值得一试。它的文档和白板融合设计确实解决了我的一部分痛点本地优先的架构也让我对数据安全更放心。但如果你想要一个开箱即用、稳定省心的工具目前可能还是商业产品更合适。AFFiNE 的迭代速度很快这意味着功能在不断完善但也意味着偶尔会遇到 bug 或者 breaking change。我自己的做法是把 AFFiNE 作为主力工具之一但不是唯一工具。重要的、长期保存的内容我会同时保留一份 Markdown 导出。这样即使工具本身出问题内容也不会丢。这个习惯我觉得对任何工具都适用不只是 AFFiNE。最后分享一个小技巧AFFiNE 的模板功能挺实用的可以把自己常用的文档结构存成模板新建的时候直接套用省去重复排版的时间。我建了几个常用模板比如“项目周报”“会议记录”“读书笔记”用起来效率高不少。
返回列表