契机
在使用 redux 的过程中, 由于业务复杂度的提升, store 里面存储的数据越来越多, 通常会有多层次的嵌套和重复数据. 同时在与后端交互的过程中, 我们经常会讨论是否要按照 UI 的层次结构来返回数据, 结果被后端驳回, 因为如果后端从数据库中取到数据后, 还要特意为前端加一层转换, 一来是考虑到服务器压力, 二来是考虑到多设备的时候无法复用接口.
在这样的契机之下, 我开始思考, 面对复杂的应用, 前端也需要处理业务逻辑, 那么数据应该如何组织和管理?
三种方案
中间层 BFF(Backend for Frontend)模型 https://samnewman.io/patterns/architectural/bff/
GraphQL http://graphql.cn/
范式化数据
这次我先讲范式化数据.
范式化
所谓 "范式化", 就是数据库设计时使用的一系列原理与技术. 在讲范式之前, 我们先说一下大家都不陌生的关系型数据库, 这就是应用范式化的典型.
关系型数据库: 建立在关系模型上的数据库.
关系模型: 若干个存储数据的二维表, 每一行称为一条记录, 每一列称为一个字段. 表与表之间用关系来描述, 有一对一 / 一对多 / 多对多三种关系.
借此两个概念很容易想到我们接触得非常多的数据库. 那关系型数据库在存储和管理数据的时候遵循哪些原则呢, 见下:
第一范式 (1NF): 表列(或称属性或称字段) 具有原子性, 不可再分.
第二范式 (2NF): 满足 1NF 的前提下, 表行(或称实例) 必须具有唯一可以被区分的主键 key
第三范式(3NF): 满足 1NF&&2NF 的前提下, 每个表中不包含已在其他表中定义的非关键字段, 也就是不能有冗余数据.
前端范式化数据
明白了范式化的概念与设计原则, 我们以 Redux 应用为例, 看一下我们应该怎样设计范式化数据, 先看一个简单的例子, 我们组织数据通常是这样:
我们分析以上这个例子, 可以看出实体有 article/author/comment/commenter, 对应设计为数据库表的时候可以建三张表 user/comment/article, 所以范式化数据应该像这样:
总结:
每种类型的数据应该具有自己的表,
每张表存储在对象中, 对象中的每个元素即为一条记录, 用 id 作为 key, 数据内容作为 value
数据 A 与数据 B 的关系通过 ID 来描述
这样带来的好处显而易见:
数据更加扁平化, 最多只有一层
数据间的关系清晰
数据项是唯一的, 减少冗余
reducer 不再需要深层次嵌套处理数据
更新数据项时, 更新只会发生在当前项, 不会对引用该数据项的地方做无必要的重复渲染
每个组件可以 connect 自己的数据, 无需一层一层的向下传递数据(可提高性能)
应用
当服务器端把数据传到前端的时候, 我们不太可能手动的去把这些数据范式化, 好在我们已经有了很多成熟的库, 帮我们做转化. 在 Redux 应用中, 推荐比较多的就是 Normalizr 了, 这个库主要是帮我们将数据做范式化处理, 输出的数据可以放在 state 中, 显然, 这样的范式化数据在 store 中进行手动处理, 会非常不方便, 也有这样的库来帮助我们解决范式化数据在 store 中的问题, 比如 Denormalizr https://github.com/gpbl/denormalizr . 但是还有一个更好的工具, Redux-ORM https://github.com/tommikaikkonen/redux-orm , 它可以说是 Normalizr 和 Denormalizr 的结合(如果是 vue, 也有类似的工具 https://github.com/vuex-orm/vuex-orm ).
结束语
一直认为学习要在熟悉掌握基础的前提下多加实践, 所以我这一部分只是讲了一些范式化的基础概念, 同时引出生态链中与此相关的受欢迎的库. 第二部分我将详细讲一下我的 Redux-ORM 实践. 欢迎小伙伴们一同探讨
参考资料:
https://zhuanlan.zhihu.com/p/36487766
来源: https://juejin.im/post/5c8602165188257f3d5d07a5