场景:一个可视化的“模型训练流水线”
想象一下你负责的“模型训练流程”模块,它看起来像这样: 左侧是“算子选择区”:展示了各种可用的算子,比如“数据清洗”、“特征提取”、“模型训练”等。
中间是“画布区”:用户可以从左侧拖拽算子到画布上,并用线连接它们,形成一个工作流。
右侧是“属性配置区”:当用户点击画布上的某个算子时,右侧会显示该算子的详细参数,比如“学习率”、“迭代次数”等,用户可以在这里修改。
顶部是“控制栏”:有一个“开始训练”按钮和一个“训练状态”的实时显示(如:空闲、训练中、成功、失败)。 在这个场景下,我们的 state 可能长这样:
技术选型:状态管理上:有意进行渐进式的迁移。
原生useState、原生provider、Redux、Zustand、Jotai
几个方案: Redux的话,从来不是比较合理的选择,模版代码太多,用最新的tookit和各种中间件库之后,心智负担还是太重。任何情况下都不考虑。
原生State:全局状态管理简直噩梦。 Provider:涉及context的拆分,一不注意就会出现大量的重渲染,简直是噩梦。
Zustand+useReducer 混合方案:基于架构角度考虑,Zustand负责全局状态管理,Provider负责我所说明的这个场景下的这个模块的状态管理。
- 然后剩下两个方案,只用jotai和只用Zustand。
- 我们业务里面几乎只用useState,甚至会出现一个State传递多层的情况出现,我是基于项目想长期维护的角度,想给他进行渐进式的迁移。最后选择了纯用jotai,它是一个原子化的状态管理框架,从useState切换过来几乎没有成本。
- 然后,因为要管理图表的每个节点的状态,所以用jotai这种原子化的状态,十分方便。
无与伦比的组合能力:Jotai 的派生原子非常强大。我们可以轻易地创建一个派生原子来表示‘当前选中的算子’,或者另一个派生原子来计算‘当前画布是否合法’。这种将状态和计算逻辑组合起来的能力,使得构建复杂但清晰的数据流变得非常简单
技术选型:图谱库
在敲定状态管理库之后,我现在要针对算子工作流来选择一个图谱库,备选项是React Flow、antdv6和x6以及D3.js等等
从业务拓展角度来看: 因为这个业务主要就是模型算子工作流,不涉及更复杂的图,比如树状图,力导向图等等,工作流的节点也不多,所以react-flow就足够满足需求了;
从迭代速度角度来看:react-flow的实现成本最低
React Flow antdv6/x6 D3.js
技术选型:拖拽库
react-dnd dnd-kit react-beautiful-dnd
状态管理:
Redux/Zustand/useReducer 的世界观:状态是一个(或多个)巨大的、集中的对象 (Store)。你通过 selector 从这个大对象里取出你想要的一小片数据。这是一种“自上而下”的模式。 Jotai 的世界观:状态本身就是由无数个微小的、独立的状态单元(原子)组成的。没有一个中心化的 Store。组件直接订阅它所需要的那个或那几个原子。这是一种“自下而上”的模式。