技术解读
Triplit 深度解析:把数据库塞进客户端,是野心还是解药?
项目背景与痛点
前端数据层的复杂度,几乎全部来自一个事实:权威数据住在远端,而 UI 需要它立刻可用。于是我们发明了一整套补偿机制——React Query / SWR 做缓存、手写乐观更新、自己维护 invalidation key、用 WebSocket 接收推送再手动 patch 缓存。业务代码里塞满了"如果请求失败就把本地状态回滚"的胶水,而这些胶水往往比业务逻辑本身还多。
Firebase、Supabase、Convex 这类 BaaS 把实时订阅做掉了,但代价是把数据主权和部署形态交给厂商。离线支持要么缺失,要么是"缓存一份 JSON"级别的敷衍——你没法在客户端对缓存做带 where 和关系 join 的查询。
Triplit 的切入点很直接:不要把同步当作网络层的问题,要把它当作数据库的问题。它在客户端跑一个真数据库(IndexedDB / SQLite / 内存),在服务端跑另一个(SQLite / Cloudflare Durable Objects),中间用 WebSocket 做双向增量同步。上层只面对一套 schema 和一套查询 API——查询即订阅,结果随数据自动重算。
核心技术揭秘
理解 Triplit 的关键,在于它的存储模型:它不是一个行式/文档式存储,而是属性级的实体-属性-值(EAV)三元组。每个变更的单位是「某实体的某个字段」。
这个选择几乎决定了后续所有能力。字段级增量意味着同步包可以极小;意味着读权限可以精确到列;意味着两个客户端同时改同一实体的不同字段时不会互相覆盖。代价是查询执行需要更多重组工作,但换来的是同步层和权限层的巨大简化——这是典型的「把复杂度挪到本地」的取舍。
同步的闭环大致如下:
几个值得拆开说的设计点:
查询订阅的匹配索引。 服务端并不广播全量变更,而是维护一张「活跃订阅表」:每条订阅记录其 collection 与过滤条件,写入时按 collection 分桶再做谓词匹配,只把命中的变更推给对应连接。这就是为什么 Triplit 能在服务端支撑大量细粒度订阅——本质是一个内存中的发布/订阅路由表,而不是简单的广播房间。
Durable Objects 版本是个聪明的架构妥协。 Cloudflare 的 DO 天然保证单实例、单写者、串行执行,这恰好解决了边缘部署中最麻烦的协调问题:同一个 DO 内部的订阅与写入天然一致,不需要额外锁。代价是放弃了跨 DO 事务,扩展性靠「选对分区键」而非全局一致。这不是缺陷,是明确的取舍。
乐观更新 + 服务端裁决的收敛模型。 客户端先写本地、先渲染,变更进入队列等待服务端确认;被拒绝则回滚并触发订阅重算。冲突策略是务实的 last-write-wins 风格,而非 CRDT——这决定了它做不了文本协同编辑这类语义合并场景,需要外挂 Yjs 之类的方案。
schema 单一来源。 TypeScript 定义的 schema 同时驱动客户端类型推导、服务端校验和数据库迁移,查询结果的类型是从 schema 推导出来的而非手写泛型。这一点对开发体验的加成,比任何性能数字都重要。
功能亮点与差异
第一,可自托管。 这是它与 Firebase/Supabase 最大的分野:一个 Node 进程 + 一个 SQLite 文件就能起服务端,不必把数据交给第三方。对企业内网、合规敏感场景,这条几乎是一票否决项。
第二,存储可插拔但 API 一致。 IndexedDB、SQLite、Durable Object 背后是不同的运行时(浏览器、React Native/Expo、Workers),但上层查询语义保持一致。这需要相当克制的抽象层设计——把 SQL 方言差异封在适配器里,而不是泄漏到查询 API。
第三,关系查询在客户端完成。 支持带关联的嵌套查询与订阅,本地 join 而非多次往返。这让"列表 + 详情"类 UI 的实现从 N 次请求变成一次订阅。
第四,绑定层极薄。 React / Solid / Vue / Svelte 各自一个包,做的是把订阅接到各框架的响应式原语上,而不是重造一套状态管理。
应用场景与落地建议
适合:离线优先的协作工具(任务板、笔记、CRM)、弱网/移动端应用、边缘低延迟场景、以及需要数据自托管的企业内部系统。
不适合:需要跨实体强事务的记账类业务;海量数据的分析型查询;以及需要语义合并的富文本协同(LWW 会丢编辑)。
生产环境几条硬建议:
- 同步范围要克制。 客户端只能查询已同步下来的数据,范围设计过宽会导致首屏拉取爆炸。按用户/租户/项目做分区,而不是"整库同步"。
- 权限规则必须在服务端执行,别信客户端。 客户端校验只是体验优化。
- schema 迁移是真实痛点。 客户端存在多版本共存(用户不刷新页面),变更需要向前兼容,破坏性改动要分多步上线。
- 监控订阅数、下行字节和回滚率。 回滚率异常升高通常意味着规则写错或冲突策略不适配业务。
- 接受它是一个仍在快速演进的项目。 API 稳定性需要自行评估,重要路径建议做一层薄封装隔离。
综合评价
优点:架构方向正确——把同步下沉到数据层,而不是让每个业务页面自己解决;EAV 模型带来的字段级同步与权限是真正的差异化能力;自托管 + 可插拔存储让它能进企业;类型链路从 schema 贯通到查询结果,工程体验在线。
缺点:LWW 冲突策略决定了它够不到协同编辑的深水区;客户端数据边界要求开发者提前想清楚同步范围,学习曲线不在 API 而在"分布式数据建模"这个思维转换上;生态与社区规模尚小,遇到边缘 case 基本只能读源码;0.x 阶段的 API 演进会带来迁移成本。
一句话总结:它不是又一个 Firebase 克隆,而是把"本地优先"从理念做成了可用的工程实现。 如果你的痛点是"实时数据 + 离线可用 + 不想被云绑定",它值得认真评估;如果你要的是强一致事务或文本级协同,它现在还不是答案。

评论