javascript中最常用的数据结构中肯定有对象。从对象中取数据,往对象中存数据,都是最常见的操作。对象可简单,可复杂。既可以存结构简单的一些数据,也可以生成结构复杂的数据。理论上来说,前端的数据管理都可以基于一个{}来实现。现实里面我们通常会通过一些库来做数据管理,比如mobx、redux和vuex。
这些库都基于各自的规则实现了:读取、修改、变更通知。比如redux(笔者对redux比较熟悉,所以以redux举例)的所有数据就是一个内部维护的对象state。想要获取state某部分的数据,就是正常的对象属性读取。如果数据在state比较深的地方,读取路径就比较长。除了读取,修改较深层级的数据也一样。读取和修改较深层级的数据是一件比较麻烦的事情。因此在实际的开发过程中我们都会尽量避免超过写出层级超过三层的数据。这里的路径信息本身也是数据的一部分。但在平常的业务开发中,对“路径信息”本身的消费不占大多数,占大多数的是节点本身。
之前为了解决不用人工的处理“路径信息”,将对“路径信息”的处理交给了程序处理,只用知道节点是哪一个就行。详情见
滚滚:数据管理工具--femo(front-end model)zhuanlan.zhihu.com![7df6d8a18a942e7592029af8c0be9957.png](https://img-blog.csdnimg.cn/img_convert/7df6d8a18a942e7592029af8c0be9957.png)
最近在项目使用femo时,发现“路径信息”在我想要使用哪个数据节点时,几乎没有用处。那么为什么不去掉?把树形结构的数据组织形式变成分散的数据点。
我带着这个想法重新思考femo的设计
滚滚:面向数据设计的前端状态管理库zhuanlan.zhihu.com![a083f368f931403d6f76f717217d93b1.png](https://img-blog.csdnimg.cn/img_convert/a083f368f931403d6f76f717217d93b1.png)
树形结构不仅带来了设计上的冗余,而且提升内部的复杂度:
- 在使用数据节点时基于引用,按引用记录的路径从全局state读取。
- 基于树形的全局state对应了同样结构的model组成的树形结构。
- 为了实现数据节点的复用,扩展全局state,将model也嵌套进了初始值的树形结构里面。
- 引用不变性这一特性没有能在gluer定义的数据节点上使用,容易导致问题。
- 为了完全避免4带来的不便性,提供了referToState方法,根据model引用索引数据。
其中1、4、5属于冗余设计,2、3是较大的提升了复杂度但没带来明显的收益。
有这些想法后大概10来天,开始动手改造:
- 将全局state按数据节点拆分,每个数据节点维持自身数据。不再有一个全局统一的state。
- 数据节点直接之间没有嵌套关系,但能够组合调用。
- 数据节点现在就对一个唯一的引用。
- 数据节点本身可以返回最新数据。
于是代码就变成了这样:
import { gluer, subscribe } from 'femo';
const name = gluer('初始名字');
const unsubscribe = subscribe([name], (nameData) => { console.log(nameData) });
// 会打印 张胜男
name('张胜男');
// 获取最新的名字 张胜男
name();
// 取消监听。调用返回的函数即可
unsubscribe();
更多源码戳这里
树形全局对象拆分成一个一个独立的小块数据,有点像多store模式,但没有store这么重。小块数据是一种只专注于自身数据的输入、处理和输出的模型,会比较轻巧。
最近在项目中和react hook结合使用femo比较多,react hook抽离封装状态和行为逻辑的能力让我印象深刻。这种状态与逻辑的组合能力,也让我看到了离散的状态有更强的灵活性和组合能力。开始反思femo中的一些模式,遂有了上面的一些想法。