新闻详情

新闻详情

首页 / 资讯中心 / 详情

AppFlowy开源架构解析:Flutter+Rust如何实现本地优先的Notion替代

发布时间:2026/9/26 14:46:42来源:尧图网络
AppFlowy开源架构解析:Flutter+Rust如何实现本地优先的Notion替代
1. 一个让我彻底放弃订阅制的开源项目第一次认真审视 AppFlowy是因为团队里一位同事在群里甩了张截图一个几乎和 Notion 长得一模一样的界面左侧是层级页面树中间是块编辑器右侧还能拉出数据库视图。他配了一句话——“这玩意儿本地跑数据在自己硬盘上不要月费。”当时我的第一反应是怀疑毕竟这些年打着“Notion 替代品”旗号的项目我见过太多大多停在 demo 阶段要么编辑器卡顿要么数据库功能残缺要么同步逻辑一塌糊涂。但 AppFlowy 让我改观的地方在于它不是简单抄一个界面而是把 Notion 最核心的三件事——块编辑器、数据库视图、本地优先的数据所有权——用一套完全开源的架构重新实现了一遍。这篇文章我想聊的不是“AppFlowy 有多好”而是从一个实际折腾过它、也踩过坑的从业者角度拆解它凭什么敢叫板 Notion它的技术选型背后是什么逻辑块编辑器到底难在哪数据库视图是怎么做出来的本地优先和协作同步如何共存以及普通用户和开发者分别能从中拿到什么。如果你正在找一个能自己掌控数据、又能二次开发的笔记与知识库工具或者你本身就是做前端、做桌面端、做协同编辑的开发者这篇内容应该能给你不少可直接参考的东西。我会尽量把每个关键决策背后的“为什么”讲透而不是只丢一堆功能列表。2. 先搞清楚 AppFlowy 到底在解决什么问题2.1 Notion 的爽点与痛点一句话说清Notion 的成功不是偶然。它把“文档”和“数据库”揉成了一个东西你写的每一段文字是一个 block你建的每一张表也是一堆 block 的集合于是你可以在一篇文档里嵌入一个看板在看板卡片里再写一篇文档层级无限嵌套。这种“万物皆块”的模型配合极低的上手门槛让它成了很多人管理知识、项目、甚至生活的中枢。但它的痛点同样明显。第一是数据不在你手里所有内容存在别人的服务器上导出虽然支持 Markdown 和 CSV但导出后块结构、数据库关联、页面层级经常丢失等于“能带走文字带不走结构”。第二是订阅成本团队协作按人头按月收费人一多就是一笔固定支出。第三是扩展性你没法给它加一个自己想要的视图类型也没法把内部数据接到自己的系统里做二次处理。这三点恰好是开源项目最擅长切入的地方。2.2 AppFlowy 的定位不是复刻是重写AppFlowy 官方给自己的定位很明确一个开源的、本地优先的 Notion 替代品。注意“本地优先”这四个字它不是“只能本地”而是“本地是主云端是辅”。你的数据默认存在本地 SQLite 里想同步再开同步服务。这和 Notion 的“云端是唯一真相源”是两种完全不同的架构哲学。从技术栈上看它做了一个相当大胆的选择前端用 Flutter核心逻辑用 Rust两者通过 FFI 打通。这个组合在笔记类开源项目里并不常见大多数同类项目要么纯 WebElectron React要么纯原生。AppFlowy 选 Flutter Rust背后有很清晰的考量我在下一节会详细拆。2.3 谁适合用它谁不适合先说适合的对数据隐私敏感、希望内容存在自己设备上的人需要把知识库接入自己工作流、做二次开发的团队预算有限但想要数据库视图能力的小团队以及想学习块编辑器、CRDT 协同、跨端架构的开发者。再说不太适合的追求开箱即用、完全不想碰命令行和配置的纯小白需要极其成熟的多人实时协作、且对稳定性要求苛刻的企业以及依赖 Notion AI、复杂公式、大量第三方集成的重度用户。AppFlowy 在这些方面还在追赶认清边界比盲目吹捧更重要。3. 技术选型拆解为什么是 Flutter Rust3.1 跨端一致性一套 UI 跑遍桌面和移动Notion 的桌面端本质是 Electron 套壳好处是 Web 技术栈复用坏处是内存占用高、启动慢、原生体验差。AppFlowy 用 Flutter 做 UI最大的收益是同一套代码能编译到 Windows、macOS、Linux、iOS、Android。对于一个小团队来说这意味着不用为每个平台维护一套界面代码迭代速度能快很多。Flutter 的自绘渲染引擎在这里也帮了忙。块编辑器需要频繁重绘、精确控制光标和选区如果用 WebView 方案光标定位和输入法兼容问题会非常头疼。Flutter 直接操作渲染层输入响应更接近原生。当然代价也有Flutter 桌面端的文本输入在早期版本里对中文输入法的支持并不完美这是后话我在问题排查那节会讲。3.2 核心逻辑用 Rust性能与安全的双重考量把数据层和业务逻辑放在 Rust 里是 AppFlowy 架构里最关键的一步。原因有三。第一是性能块编辑器的文档模型在频繁编辑时会产生大量增量操作用 Rust 处理这些操作、维护内存中的文档树比在 Dart 或 JS 里做要快得多尤其是文档变大以后差距明显。第二是安全Rust 的所有权模型能在编译期挡掉大量内存安全问题对于一个要长期维护、还要处理用户数据的项目来说这是实打实的优势。第三是复用Rust 核心可以被编译成不同平台的库未来如果要接 Web 端通过 WASM逻辑层几乎不用重写。Flutter 和 Rust 之间通过 FFI外部函数接口通信。简单理解就是Dart 层负责“画”和“接收用户输入”Rust 层负责“算”和“存”两边通过定义好的接口传数据。这个边界划得很清楚UI 归 UI逻辑归逻辑谁出问题查谁不会搅在一起。3.3 本地优先架构SQLite 做底座AppFlowy 本地用 SQLite 存数据这一点很务实。SQLite 是嵌入式数据库不需要单独起服务单文件、跨平台、事务可靠非常适合桌面和移动端。文档的块结构、数据库的行列、页面的元信息都落在 SQLite 的表里。你打开 AppFlowy 看到的每一页内容本质都是从本地这个文件里读出来的。本地优先带来的直接好处是断网可用、打开快、数据可备份直接拷那个数据库文件就行。代价是同步变复杂了因为多台设备各自改各自的本地库合并时必然产生冲突。这就引出了下一节要讲的 CRDT。4. 块编辑器整个项目最难啃的骨头4.1 什么是块为什么不用富文本传统富文本编辑器比如早期的 Word把一整篇文档当成一个 HTML 或一段带格式的文本改一个字可能触发整段重排。块编辑器换了个思路文档是一串 block 的列表每个 block 是一个独立单元可以是一个段落、一个标题、一个待办、一张图片、一个数据库视图。你编辑某个 block只影响这个 block 自己。这个模型的好处是结构清晰、可嵌套、可拖拽重排、可单独设置属性。Notion 的核心体验就建立在这上面。但实现难度也高光标要能跨 block 移动选区要能跨 block 选择删除一个 block 要处理前后合并粘贴富文本要解析成 block 序列。这些细节每一个都是坑。4.2 文档模型的数据结构AppFlowy 的文档在内存里是一棵树。根节点是页面页面下面是若干 blockblock 可以有自己的子 block。每个 block 有唯一 ID、类型、属性比如标题级别、待办是否勾选、以及内容。Rust 层维护这棵树所有编辑操作都转化成对这棵树的增删改。这里有个关键设计编辑操作不是直接改树而是生成“操作事件”operation再由这些事件去更新树和数据库。这样做的好处是操作可记录、可回放、可同步。你在 A 设备上敲了一个字生成一个 insert 操作这个操作既能更新本地树也能发给其他设备去应用。这就是协同编辑的基础。4.3 光标、选区与输入法最容易被低估的细节块编辑器里光标不是一个简单的 index。它需要知道自己在哪个 block、block 内的哪个位置、是折叠还是展开状态。跨 block 选择时选区要记录起点和终点分别在哪个 block 的哪个偏移。这些状态在 Flutter 层和 Rust 层之间来回同步稍有不慎就会出现光标跳位、选区错乱。中文输入法更是重灾区。输入法在拼音阶段会有一个“预编辑”状态这时候字符还没真正上屏。如果编辑器在预编辑阶段就触发了 block 更新很容易出现拼音被打断、候选框位置错乱的问题。AppFlowy 在这块做过多次修复核心思路是把预编辑状态和正式提交分开处理预编辑期间不生成正式操作。提示如果你自己在做块编辑器输入法兼容一定要在项目早期就纳入测试等到功能堆完再回头修成本会高好几倍。4.4 拖拽与嵌套交互背后的数据操作拖拽一个 block 到另一个位置表面上是 UI 动画底层是一次“移动操作”从原父节点的子列表里移除插入到新父节点的指定位置。如果拖拽的是带子节点的 block整棵子树要一起移动。嵌套则更微妙把一个 block 拖到另一个 block 内部等于改变了父子关系文档树的深度变了渲染时的缩进、折叠状态都要跟着更新。我实测下来AppFlowy 的拖拽在文档不大时很流畅但当一个页面有几百个 block、嵌套好几层时偶尔会有轻微卡顿。这属于可以接受的范畴毕竟 Notion 在超大文档下也会卡。5. 数据库视图把表格、看板、日历统一起来5.1 数据库的本质是“带 schema 的块集合”Notion 的数据库看起来像表格但它不是传统意义的表。每一行其实是一个页面每一列是一个属性。AppFlowy 沿用了这个思路一个数据库视图底层是一组“行 block”每个行 block 挂着一组属性值。视图只是这些数据的不同呈现方式同一批数据可以同时用表格看、用看板看、用日历看。这种设计的好处是数据只有一份视图随便切。坏处是实现复杂度高因为你要为每种视图写一套渲染和交互逻辑还要保证它们操作的是同一份数据。5.2 表格视图排序、筛选、属性类型表格视图是最基础的。AppFlowy 支持文本、数字、单选、多选、日期、复选框等属性类型。排序和筛选是在数据层做的不是简单地对渲染结果排序。比如你按日期列降序Rust 层会拿到所有行、读取日期属性、排序后再返回给 UI。属性类型的设计有个细节值得说单选和多选的值不是直接存字符串而是存选项 ID选项本身有独立的定义名称、颜色。这样改选项名称时所有引用它的行自动更新不用逐行改。这是数据库设计里很经典的一招做知识库工具一定要这么干。5.3 看板视图分组逻辑与拖拽换组看板视图按某个单选属性分组每个选项一列行作为卡片分布在列里。拖拽卡片从一列到另一列本质是修改这行的分组属性值。听起来简单但要处理空列、未分组项、列内排序还要保证拖拽后数据立即持久化。我踩过的一个坑是看板列的顺序和分组选项的顺序是两回事。分组选项有它自己的排序看板列可以单独拖拽调整显示顺序。如果实现时把两者混在一起用户调整列顺序会意外改掉选项顺序体验很割裂。AppFlowy 把这两个顺序分开存算是处理得比较干净。5.4 日历视图与未来扩展日历视图按日期属性把行铺到日历格子里适合做日程、内容排期。AppFlowy 目前还在持续完善视图类型社区里也有人在做甘特图、画廊视图的插件。这种“核心提供基础视图扩展交给社区”的思路和 Notion 早期很像也是开源项目能快速铺开功能面的有效方式。6. 本地优先与协作同步CRDT 到底怎么用6.1 为什么不用“锁 覆盖”的老办法传统同步方案是谁先改谁赢后改的覆盖先改的或者加锁防止同时改。这在文档协作里行不通因为两个人同时编辑同一段文字是常态简单覆盖会丢内容。于是就有了 CRDT无冲突复制数据类型。CRDT 的核心思想是每个操作都带足够的信息使得不同设备上的操作无论以什么顺序应用最终结果都一致。比如插入字符时带上位置标识删除时标记删除而不是真删这样合并时不会互相打架。6.2 AppFlowy 的同步模型AppFlowy 的同步是“本地库 操作日志 同步服务”的组合。本地编辑生成操作操作先落本地再通过同步服务传到其他设备。其他设备收到操作后应用到自己的本地库。因为操作本身是 CRDT 友好的所以即使两台设备离线各改各的重新联网后也能合并。这里要强调一点AppFlowy 的同步服务是可以自建的。官方提供了服务端实现你可以部署在自己的服务器上数据全程不经过第三方。对于团队来说这是数据主权的重要保障。6.3 离线编辑与冲突合并的实际表现我做过一个测试两台设备都断网各自在同一篇文档的不同位置插入内容然后恢复网络。结果是两边的内容都保留了没有丢失位置也基本正确。这说明它的 CRDT 实现是能扛住真实场景的。但也有边界情况。如果两个人同时修改同一个 block 的同一个属性比如同时改标题最终会有一个胜出另一个被覆盖。这是 CRDT 在“最后写入胜出”语义下的正常行为不是 bug。理解这一点才能合理预期协作行为。6.4 自建同步服务的注意事项自建同步服务不是点一下就行。你需要一台能长期运行的服务器、配置数据库、处理备份、考虑访问控制。我的建议是个人用户如果只是自己多设备同步可以先不折腾用官方提供的同步方式或者干脆手动同步数据库文件团队用再考虑自建而且要提前规划好备份策略因为同步服务一旦数据损坏影响的是所有设备。7. 实操从零把 AppFlowy 跑起来并做二次开发7.1 环境准备与依赖安装想自己编译 AppFlowy需要准备这些Flutter SDK版本要匹配项目要求、Rust 工具链rustup 装好、以及对应平台的构建工具Windows 要 Visual Studio 的 C 组件macOS 要 Xcode 命令行工具Linux 要 clang 和 GTK 相关库。这些依赖缺一个都编译不过而且报错信息往往不直观。我的经验是先把 Flutter 的flutter doctor跑到全绿再装 Rust最后拉代码。顺序反了容易在环境问题上浪费大量时间。7.2 编译与运行的关键命令拉下代码后典型流程是这样git clone 项目仓库地址 cd appflowy flutter pub get # 编译 Rust 核心 cargo build --release # 运行桌面端 flutter run -d windows # 或 macos / linuxRust 核心的编译第一次会比较慢因为要下载和编译大量依赖。之后增量编译就快了。如果编译 Rust 时报链接错误八成是平台构建工具没装全回去补依赖。7.3 目录结构速览改代码该去哪理解目录结构能省很多时间。大致上Flutter 的 UI 代码在frontend相关目录Rust 核心在rust-lib或类似命名的目录两者通过生成的 FFI 绑定连接。数据库 schema、迁移脚本通常在 Rust 侧。想改界面去 Flutter 目录想改数据逻辑、加属性类型、调同步策略去 Rust 目录。注意改 Rust 接口后FFI 绑定需要重新生成别忘了这一步否则 Dart 层调不到新接口会报找不到符号。7.4 加一个自定义属性类型的思路假设你想给数据库加一个“评分”属性1 到 5 星。步骤大致是在 Rust 侧定义新的属性类型枚举和它的序列化格式在数据库 schema 里支持这种类型的存储在 Flutter 侧写对应的单元格渲染和编辑组件最后在属性类型选择列表里注册它。整个过程是“数据层先通UI 层后接”顺序别搞反否则 UI 做完了发现底层存不了白干。8. 常见问题与排查技巧实录8.1 编译与运行类问题现象可能原因处理方式Rust 编译链接失败平台构建工具缺失补装 C 构建工具或 Xcode 命令行工具Flutter 找不到设备桌面支持未开启执行flutter config --enable-平台-desktop启动后白屏FFI 库未正确加载确认 Rust 库已编译且路径正确中文输入异常输入法预编辑处理问题升级到较新版本或检查输入法兼容设置8.2 数据与同步类问题数据不同步先查三件事本地数据库文件是否正常、同步服务是否可达、操作日志有没有堆积。我遇到过一次同步卡住最后发现是本地库文件被外部程序占用导致写入失败。关掉占用程序后恢复正常。另一个常见问题是“同步后内容重复”。这通常发生在操作被重复应用时。排查思路是看操作日志里有没有重复的操作 ID。如果自己改过同步逻辑重点检查去重机制。8.3 性能类问题文档变大后卡顿主要出在渲染和操作计算两处。渲染侧可以靠虚拟列表只渲染可视区域的 block计算侧要避免每次编辑都全量重算文档树尽量做增量更新。AppFlowy 在这两块都有优化但如果你自己往里加功能很容易不小心引入全量操作把性能拖垮。8.4 我的几条避坑心得第一别在没跑通官方版本之前就动手改代码先确保原始项目能编译能运行再谈二次开发。第二改 Rust 核心前先写测试块编辑器和同步逻辑的回归成本极高没有测试兜底很容易改出新 bug。第三数据库 schema 变更一定要走迁移脚本别手动改表结构否则用户升级时数据会出问题。第四多设备测试要趁早单机跑得再顺一到多设备同步就可能暴露一堆问题。9. 它凭什么叫板以及我的实际体会AppFlowy 敢叫板 Notion靠的不是功能数量堆得比对方多而是抓住了一个 Notion 结构上不会让步的点数据所有权。Notion 的商业模式决定了它必须把数据放在云端而 AppFlowy 从架构第一天起就把本地优先写进了骨子里SQLite 做底座、Rust 管逻辑、同步可选可自建。这个差异不是靠加功能能抹平的它是路线之争。再加上开源带来的可扩展性你可以加自己的视图、接自己的系统、改自己的同步策略这是闭源产品给不了的。块编辑器和数据库视图这两块最难啃的骨头它也确实啃下来了虽然细节上还不如 Notion 打磨得那么圆润但核心体验已经能打。我在实际使用中的体会是把它当成一个“能自己掌控、能自己改”的知识库底座而不是一个即开即用的成品心态就对了。它的价值不在于今天比 Notion 好用多少而在于它给了你一条不依赖别人的路。对于愿意折腾的团队和开发者这条路值得走。最后分享一个小技巧如果你只是想先体验别急着编译直接下官方发布的安装包跑一遍感受完核心交互再决定要不要深入源码能省下不少时间。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

NC57+Oracle10g在Win2012R2上的兼容部署实战 2026/9/26 15:25:00

NC57+Oracle10g在Win2012R2上的兼容部署实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
尼康VMR-1515影像测量仪二手采购与实操精度解析 2026/9/26 15:24:59

尼康VMR-1515影像测量仪二手采购与实操精度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
MySQL 8.0 实战学习路径:Docker 环境搭建+故障排查+性能分析 2026/9/26 15:24:53

MySQL 8.0 实战学习路径:Docker 环境搭建+故障排查+性能分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
2025年从微软官网手动下载Win10原版ISO完整指南 2026/9/26 15:24:47

2025年从微软官网手动下载Win10原版ISO完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Excel双击才生效?揭秘单元格格式与存储值机制及批量转换方案 2026/9/26 15:24:40

Excel双击才生效?揭秘单元格格式与存储值机制及批量转换方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
如何“训练” Codex 的 Skill:从 SKILL.md 到 config.toml 的实战配置 2026/9/26 15:24:40

如何“训练” Codex 的 Skill:从 SKILL.md 到 config.toml 的实战配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉