Redux state设计原则:像设计数据库一样设计state

前言

在数据库的设计中,数据库是由1到多个数据表构成的,数据表就是用来存储数据的。在功能与Redux有些微的类似,都是用来保存数据的,而且这些数据在不设权限的情况下是共享的。

在前辈开发人员的探索下,发现Redux state最好的设计方式就是像数据库一样设计。

数据库是怎样设计的

来看一个简单的例子:

上图是一张表格,在这张表格中有一个数据列的名称是“标识”。可以看到这一列的标识中,每一项都是不同的数字。

而这样每一个不同的数字,对应着横向一行数据,它们的关系是一对一的关系。每一个唯一标识都对应着唯一的数据项。

那么在查找数据的时候就可以通过这个唯一的“标识”来查找与之唯一对应的数据项(可能包含一个数据,也可能包含多个数据),在后端领域,将这个“标识”称之为主键。以下是对“主键”的科学定义:

主键,又称主码(英語:primary key或unique key)。 数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。 一个数据表只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。

以上都是比较直白话的说法,下面这三点是比较官方化的数据库设计基本原则的介绍:

  1. 数据按照领域分类,存储在不同的表中,不同的表中存储的列数据不能重复。
  2. 表中的每一列的数据都依赖于这个这张表的主键。
  3. 表中除了主键以外的其他列,互相之间不能有直接依赖关系。

Redux state该如何设计

上面已经说过了,Redux state的设计原则应该是仿照数据库设计原则的。所以,针对以上数据库基本设计原则,套用在Redux state的设计中,可以得到如下结论:

  1. 数据按照领域把整个应用的状态按照领域分成若干个子state,子state之间保存的数据是不重复的。
  2. 表中的state以键值对的结构存储数据,以记录的key/ID作为记录的索引,记录中的其他字段都依赖于索引。
  3. state中不能保存可以通过已有数据计算而来的数据,即state中的字段不互相依赖。

示例

如果现在有一个文章列表,后端以数组的形式将数据传给前端,传递的数据是这样的(暂时称之为结构体):

{
    posts: [
        {
            id: 1,
            title: 'First Blog',
            content: 'some thing',
            comments: [
                {
                    id: 454,
                    userId: 23,
                    content: 'haha',
                },
                {
                    id: 759,
                    userId: 23,
                    content: 'baibai',
                }
            ]
        }
    ]
}

使用上面所讲的设计原则,可以将这样的数据做扁平化操作,结果如下(暂时称之为扁平体):

{
    posts: {
        1: {
            id: 1,
            title: 'First Blog',
            content: 'some thing',
            commentIds: [454, 759],
        }
    },
    postIds: [1],
    comments: {
        454: {
            id: 454,
            userId: 23,
            content: 'haha',
        },
        759: {
            id: 759,
            userId: 23,
            content: 'baibai',
        }
    }
}

上面的结构发生的什么样的改变呢?简单来说就是将数组转化成了以键值对形式存在的对象。

为什么要这样设计呢?这样设计的好处是什么?原因是扁平化的state,具有更好的灵活性和扩展性

这样的一句说明可能还不太好理解这样改写的好处,现在在此基础上提出一个需求,来帮助理解这么设计的好处究竟在哪里。

产品经理:“需要允许用户在原来的评论上进行修改。”
程序员:“了解。”

需求到位,首先需要获取到用户原来的评论,当然可以使用评论id直接请求后端数据来获取原始评论内容,但是在当前场景下从后端获取数据是不健康的方式,因为前端既然可以拿到准确的数据,就不应该调用后端的接口,给服务器增加压力。所以前端开发者需要直接从已经拿到的数据中获取原始评论内容。现在分别对这两种设计,在实现该需求上所做的工作做一下比较。

原来的数组结构拿到原始评论数据,首先需要遍历posts查找到该评论所在的文章,然后需要遍历posts下的comments,找到相应的评论对象,获取到原始评论内容。

而经过改写后的数据结构,只需要在comments对象中直接使用对象取值的方式来获取原始评论内容。

两者工作量差别之大显而易见,虽然这个需求是临时编的,但是通过两者的对比,很容易就可以发现:改写后端数据结构避免了嵌套层级过深的问题,数据查找非常方便。

当然,这种设计也并不全是好处,在某些方面也是存在相对劣势的。比如现在需要在文章列表中需要新加一篇文章,改写后的数据结构可能就需要同时操作posts和postIds这两个字段值,而改写前的数据结构只需要操作posts这一个字段就够了。

经过优劣两相对比,本文中命名的的扁平体应该是更胜一筹的。因为在实际的开发场景中,同时操作两个字段的影响并不是特别大,而嵌套层级过深的数据结构在数据修改时产生的影响是致命的。

总结

Redux state的设计原则是:像设计数据库一样设计state。

观点来源

  1. React16+Redux 实战企业级大众点评Web App - 慕课网.