引言
今天看到公众号推送的一篇文章《前端数据范式化》,觉得很有意思(其实是无知~)。便以此为起点,翻阅了很多优秀博主的技术分享,试图去理解范式的含义以及对前端的意义。
范式
这里的范式指的是数据库范式。现在补习一下:
范式(数据库设计范式,数据库的设计范式)是符合某一种级别的关系模式的集合。
数据库范式分为:
- 一范式
- 二范式
- 三范式
- BCNF范式
一些概念:属性、码、函数依赖
这篇讲的细致又到位。
换个角度来看,按照面向对象的编程范式理解,每个 实例对象
就是 元组
,实例对象的 具体属性
可以理解为 属性
;码
就是其中的 一个属性或多个属性的集合
,通过这个集合可以找到这个实例对象;函数依赖
其实就是对象的 属性间的依赖关系
。
范式的设计,感觉就像是 面向对象编程时设计对象的过程
。
一范式(1NF)
定义:符合1NF的关系中的每个属性都不可再分。
以手机商品为例:
不符合示例
型号 | 参数 |
---|---|
手机1 | 白色、16g |
手机2 | 黑色、16g |
手机3 | 金色、32g |
参数 还能继续拆分成 颜色 、内存 两个属性,所以不符合1NF。 |
符合示例
型号 | 参数-颜色 | 参数-内存 |
---|---|---|
手机1 | 白色 | 16g |
手机2 | 黑色 | 16g |
手机3 | 金色 | 32g |
1NF是最基本的设计规范,但是优缺点也很明显。 优点:查找高效,只用查一个表就可以查到想要的数据。 缺点:数据冗余,插入异常,删除异常,修改异常。
二范式(2NF)
定义:在1NF的基础之上,消除了非主属性对于码的部分函数依赖。
以客户和联系人关系为例:
不符合示例
公司名称 | 公司邮箱 | 公司地址 | 联系人名称 | 联系人手机号 |
---|---|---|---|---|
公司1 | gongsi1@163.com | xx1省xx1市xx1区 | 老板1 | 111111 |
公司2 | gongsi2@163.com | xx2省xx2市xx2区 | 老板2 | 222222 |
公司3 | gongsi3@163.com | xx3省xx3市xx3区 | 老板3 | 333333 |
码是(公司, 联系人手机号)
,从 公司
可以获取到 公司邮箱
、公司地址
, 从 联系人手机号
可以获取到 联系人名称
。这个表中存在部分函数依赖,所以不符合2NF。
符合示例
公司名称 | 联系人手机号 |
---|---|
公司1 | 111111 |
公司2 | 222222 |
公司3 | 333333 |
公司名称 | 公司邮箱 | 公司地址 |
---|---|---|
公司1 | gongsi1@163.com | xx1省xx1市xx1区 |
公司2 | gongsi2@163.com | xx2省xx2市xx2区 |
公司3 | gongsi3@163.com | xx3省xx3市xx3区 |
联系人名称 | 联系人手机号 |
---|---|
老板1 | 111111 |
老板2 | 222222 |
老板3 | 333333 |
为了建立公司和联系人的关联关系,我们又拆出了客户表和联系人表,主表只存放关联关系。
三范式(3NF)
定义:在2NF的基础之上,消除了非主属性对于码的传递函数依赖。
以推荐文章中的成绩为例吧
不符合示例
学号 | 课程 | 分数 |
---|---|---|
10001 | 高数 | 99 |
10001 | 英语 | 97 |
10002 | 英语 | 88 |
10003 | 线性代数 | 77 |
学号 | 姓名 | 系名 | 系主任 |
---|---|---|---|
10001 | 德玛西亚 | 电子系 | xxx1 |
10002 | 艾克 | 物理系 | xxx2 |
10003 | 德莱文 | 数学系 | xxx3 |
通过学号
,我们可以获取到系名
;通过系名
,我们可以获取到系主任
。新拆除的表存在传递函数依赖,所以不符合3NF。
符合示例
学号 | 课程 | 分数 |
---|---|---|
10001 | 高数 | 99 |
10001 | 英语 | 97 |
10002 | 英语 | 88 |
10003 | 线性代数 | 77 |
学号 | 姓名 | 系名 |
---|---|---|
10001 | 德玛西亚 | 电子系 |
10002 | 艾克 | 物理系 |
10003 | 德莱文 | 数学系 |
系名 | 系主任 |
---|---|
电子系 | xxx1 |
物理系 | xxx2 |
数学系 | xxx3 |
BCNF范式
定义:在3NF的基础之上,消除了主属性对于码的部分函数依赖和传递函数依赖。
总结
范式的设计,是一个 复用数据
、减少冗余
的过程,个人感觉也是一个聚焦
的过程。但是,如果想查找到完整的数据,查找效率反而有所下降,从1个表的查找转变为n个表的查找。但是这样的牺牲去给增删改数据带来一定的提升,所以这种牺牲是值得的。
正常情况下,应该遵循哪个范式,笔者也没有经验,这里只是学习认识一下范式。
前端与范式
范式是对 数据
的设计,所以在前端领域,它的用处也跟数据相关。
如何使用和实践
用 normalizr的示例 举例:
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "1",
"commenter": {
"id": "2",
"name": "Nicole"
}
},
{
"id": "2",
"commenter": {
"id": "2",
"name": "Nicole"
}
}
]
}
在文章的数据中,除了 文章自身的属性
之外,还包含了 人员
、评论
两个对象的所有属性(例如人员的名称、评论的内容、评论的人员等等)。
这是符合1NF的:属性已经无法再细分了。
但是在文章的对象中,comment_id -> commenter_id -> commenter_name,author_id -> author_name。既存在部分函数依赖
,又存在传递函数依赖
。因此不满足2NF、3NF。
这样的数据,导致数据出现 冗余
,评论者的数据重复出现了2次;同时,不同对象的数据的混合增加了数据操作的复杂度
,如果想要修改人员,就得遍历
文章的每个属性(需要同时处理作者和评论人员),并且还得深入层级
(评论中的评论人员),才能进行修改。尤其像saas产品中复杂的业务数据,数据处理更加复杂。
范式化不就是解决上述的问题吗?
经过normalizr处理之后
{
result: "123",
entities: {
"articles": {
"123": {
id: "123",
author: "1",
title: "My awesome blog post",
comments: [ "324" ]
}
},
"users": {
"1": { "id": "1", "name": "Paul" },
"2": { "id": "2", "name": "Nicole" }
},
"comments": {
"324": { id: "324", "commenter": "2" }
}
}
}
之前提到的诸多问题已经不再是问题了,人员是人员,评论是评论,文章是文章。perferct ~
对象引用共享?
有同学可能会提到,在js中,一切皆对象
,既然这样,我们能不能将人员、评论改成对象引用,对象的具体数据存在一个map当中,我把上面范式化后的数据结果改了改:
"articles": {
"123": {
id: 123,
author: ["1"],
title: "My awesome blog post",
comments: [ users["324"] ]
}
},
"users": {
"1": { "id": "1", "name": "Paul" },
"2": { "id": "2", "name": "Nicole" }
},
"comments": {
"324": { id: "324", "commenter": users["2"] }
}
如果单纯是前端使用,这样做没有什么问题。但是,如果数据需要http获取和上传
,传输过程中的字节流可不像内存一样,存在这种引用关系。所以,还是老老实实按照范式的规范去组织数据,不要搞“行为艺术”。
化简为繁 ?
有开发者认为范式化是“化简为繁”,我觉得这样的认识太片面了。范式化处理最需要思考的地方是如何设计数据的schema,后续数据的处理反而很轻松。
所以繁的是前期数据范式化
的过程,简的确实渲染和业务逻辑中数据增删改
的操作。我认为这样的牺牲是值得的,如果后端同学对于下发的数据已经做了范式化处理,那前端同学连“繁”的过程都不需要了,只剩下“简”的操作。
喜欢博主的小伙伴可以加个关注、点个赞哦,今日起持续更新中!
愿天下没有难处理的数据。
Peace And Love。