- Published on
为画板 SDK 寻找合适的响应式库
- Authors
- Name
- 青雲
前言
利用周末时间,我启动了一个个人项目:从零开始构建一个画板应用。
当项目进展到核心的画板 SDK 开发时,我遇到了一个架构上的挑战。直接用 TypeScript 管理状态,会导致代码逻辑混乱,状态的传递和同步很快会变得难以维护。我想起了经典的 MVVM 架构。它能将界面(View)与数据(Model)分离,中间引入 ViewModel 处理状态和逻辑,从而提升代码的清晰度。
然而,MVVM 作为一种架构思想,需要具体的技术工具来落地,特别是实现 ViewModel 状态变更后,View 能够自动更新。这正是“响应式编程”所要解决的问题。
于是,问题就变成了:该选择哪个响应式库?
这篇文章,便是我这次技术选型的完整记录。
第一轮:初步调研
我的 SDK 是一个纯 TypeScript 项目,没有依赖 React 或 Vue 等前端框架,因此所选的库必须能够独立运行。我考察了市面上几个主流的方案:
库 | 我的第一印象 |
---|---|
MobX | 成熟的库,与 class 语法结合得很好,非常稳健。基于 Observable 对象实现响应式。 |
Vue Reactivity | Vue 3 的核心,轻量、独立,API 设计精良。基于 Proxy 和 Effect 实现。 |
Valtio | API 极简,提供优秀的开发体验。基于 Proxy 和不可变快照 (Snapshot ) 实现。 |
Signals (SolidJS) | 性能表现突出,以细粒度更新著称。基于 Signal 和精确依赖追踪实现。 |
RxJS | 功能强大,但学习曲线较为陡峭,暂不考虑。基于 Observable 流实现。 |
第二轮:筛选与归类
排除了 RxJS 后,我发现剩下的几个方案恰好代表了三种不同的设计哲学:
- MobX:面向对象风格。对于习惯使用
class
组织代码的开发者而言,它几乎是完美选择。 - Valtio:极简主义风格。追求极致的开发体验,让开发者几乎感觉不到框架的存在。
- SolidJS (Signals):性能极致风格。将性能放在首位,保证应用的运行时效率。
至此,问题变得更加清晰:我需要在这三种风格中做出权衡。
第三轮:最终对决:Valtio vs. SolidJS
我很快将最终选择的目标锁定在 Valtio 和 SolidJS 身上。
为何是这两者?因为它们分别代表了现代响应式设计的两个方向:一个将开发体验做到了极致,一个将运行时性能做到了极致。
我以画板中最常见的“画笔工具”为例,在脑海中进行了一次推演:
如果使用 Valtio :
代码会非常直观,就像在操作一个普通的原生数组。
// state.points 是一个被 proxy 包裹的数组
function onMouseMove(newPoint) {
state.points.push(newPoint); // 直接 push 即可
}
它的工作流程大致如下:
如果使用 SolidJS :
代码需要遵循 Signals 的规则,意图更加明确。
// points 是一个 signal
const [points, setPoints] = createSignal([]);
function onMouseMove(newPoint) {
// 需要通过 set 函数来更新
setPoints(p => [...p, newPoint]);
}
它的工作流程则更为精确:
对比下来,差异一目了然:
对比 | Valtio | SolidJS |
---|---|---|
开发体验 | 优秀 | 良好 |
性能 | 非常高 | 顶级 |
学习成本 | 低 | 中等 |
性能基准 | 高 (通用场景) | 顶级 (根据官方基准测试,更新计算性能领先) |
第四步:我的决定
分析至此,我心中已有了答案。我没有做一个简单的“二选一”,而是制定了一个更务实的策略。
我决定,先采用 Valtio 来启动项目。
理由如下:
- 开发效率优先:Valtio 带来的开发体验非常出色,能让我更高效地实现功能。代码也更简洁,便于后期维护。
- 性能足够强大:对于绝大多数场景,Valtio 的性能已经完全够用。我没有必要为了少数的极端情况,而牺牲掉整体的开发便利性。
当然,我也保留了备选方案。我的计划是:在开发过程中,如果确实遇到某个功能(例如大规模图形渲染)出现性能瓶颈,我才会回过头来,专门针对该模块进行性能优化。届时,SolidJS 的细粒度更新思想就能派上用场。
以下是一个简单的示例,展示如何在一个 TypeScript 项目中同时引入两者:
// core/state/valtioStore.ts
import { proxy } from 'valtio';
export const canvasState = proxy({
points: [],
currentTool: 'pen',
// ... 其他画板状态
});
// core/perf/solidSignals.ts
import { createSignal } from 'solid-js';
// 为性能敏感模块(如高频更新的图层)创建独立的 Signal
export const [highFreqLayer, setHighFreqLayer] = createSignal({
pixels: new Uint8Array(1000000), // 例如,一个巨大的像素缓冲区
version: 0,
});
通过这种模块化的方式,我可以先用 Valtio 快速搭建核心功能,再用 SolidJS 对特定瓶颈进行精准优化。
总结
这次选型过程,最大的感触是:没有最好的技术,只有最合适的。
先用 Valtio 保证开发效率,同时将 SolidJS 作为性能优化的备选方案。对我而言,这或许是现阶段最好的平衡策略。