# 从开发者视角看，如何正确为 Local First 软件添加同步功能

> 本文公开全文。

原文：https://utgd.net/article/20806

> 编者按：这篇文章的原文是写给开发者们的，对于普通用户而言可能没有那么大的参考价值。作者倡导的方案类似于“为 Obsidian 这样的本地优先软件提供 Dropbox 这样的通用云同步服务，并可以自动解决冲突”，作者一共提供了 3 种方案，其中 1 和 2 是个人用户可以实现的，而方法 3 则需要从开发层面进行功能设计了。你可以从本文中窥见 Local First 的软件为何总在同步问题上出现一些困难。

🔗 原文链接

作者参加了 Local\-First Conference，听了 Martin Kleppmann 的演讲。他提到了一些关于本地优先软件的问题，特别是数据同步的问题。

## 首先，什么是本地优先？

简单来说，本地优先软件就是：

- 数据优先存储在本地；

- 偶尔与互联网同步；

- 如果完全离线，它就是本地软件；

- 如果完全依赖互联网，它就是普通云软件。

本地优先软件的概念是优先将数据存储在本地，同时偶尔与互联网同步。这种软件让用户掌握自己的数据，但同步仍然需要在线组件，比如在个人设备之间同步数据。

然而，如果公司倒闭，同步服务将停止，这对用户来说是一个大问题。

## 利用 Dropbox 同步的几种方案

为了解决这个问题，Martin Kleppmann 提出了一个简单有效的解决方案：利用 Dropbox 或类似的云同步服务。Dropbox 是一种广泛使用、易于访问的服务，具有多种实现方式，如果一家公司倒闭，用户可以切换到其他服务。

虽然文件同步协议相对简单，不能处理复杂的同步事件或冲突解决，但通过使用 CRDT（冲突自由的复制数据类型），可以有效解决冲突。

在 Martin 的方案中，Dropbox 进行同步有三种主要实现方式。

第一种是超级简单版：将状态保存到一个文件并让 Dropbox 同步。如果在两台机器上更改状态，会出现冲突文件，通常情况下这是个问题，但使用 CRDT 可以解决冲突。CRDT 是一种可以合并数据类型，即使不完美，但总能合并数据，避免冲突。

第二种方法是每个客户端一个文件，每个客户端有自己的文件，避免了冲突，只需在本地编辑，并在其他客户端文件变化时合并数据。因为每个文件只在一个设备上编辑，Dropbox 不会报告冲突，数据冲突可以通过 CRDT 解决。

第三种方法更适合开发者：基于操作的同步。这种方式下，每个客户端记录自己的操作日志，而不是整个状态文件。想象一下，每个设备只是记录它做了哪些改变，而不是记录整个文件的内容。这些操作被写入一个单独的文件中，Dropbox 只需同步这些操作日志文件。这减少了同步负担，避免了大文件的处理。如果操作日志变得过长，可以将操作分成小块，这样 Dropbox 同步时只需处理较小的数据块，客户端也更容易跟上变化。

即使在最简单的形式下，Dropbox 作为公共同步层也能工作。然而，不同的云服务提供商处理冲突文件的方式不同，有些甚至不处理冲突，这需要依赖状态型 CRDT 来保证数据一致性。基于操作的同步方式虽然更加复杂，但通过分块操作，可以显著减少同步负担，提高效率。

尽管 Dropbox 及其他云同步服务相对简单，但足以满足基本的同步需求。虽然不如定制解决方案那么实时，但对于一般用户来说，这已经足够好，尤其是考虑到成本和长久性。

这个解决方案的亮点在于其简单有效，并且适用于大多数用户场景，特别是类似 Apple Photos 的应用：虽然不同步实时数据，但能确保所有照片最终会在各设备上出现。

在最后，Martin Kleppmann 强调，本地优先软件让用户真正掌控数据，使用简单的云同步服务作为基础设施，不仅成本低廉，还能长期使用。这个方法不仅实用，还能为用户提供一种无缝的使用体验。想象一下，如果像 Obsidian Sync 这样的应用只需将文件放入一个文件夹，就能实现免费且永久的无冲突同步，这无疑是一个极具吸引力的解决方案。

> “本地优先软件的目标是让用户掌控数据，但同步仍然需要服务器。如果公司倒闭，同步就会停止，这也是一个难题。” — Martin Kleppmann

---

[前往标准阅读页](https://utgd.net/article/20806?audience=web)
