学了一学,前端遇到范式

571 阅读7分钟

引言

今天看到公众号推送的一篇文章《前端数据范式化》,觉得很有意思(其实是无知~)。便以此为起点,翻阅了很多优秀博主的技术分享,试图去理解范式的含义以及对前端的意义。

范式

这里的范式指的是数据库范式。现在补习一下:

范式(数据库设计范式,数据库的设计范式)是符合某一种级别的关系模式的集合。

数据库范式分为:

  • 一范式
  • 二范式
  • 三范式
  • BCNF范式

一些概念:属性、码、函数依赖

这篇讲的细致又到位。

换个角度来看,按照面向对象的编程范式理解,每个 实例对象 就是 元组 ,实例对象的 具体属性 可以理解为 属性 就是其中的 一个属性或多个属性的集合,通过这个集合可以找到这个实例对象;函数依赖 其实就是对象的 属性间的依赖关系

范式的设计,感觉就像是 面向对象编程时设计对象的过程

一范式(1NF)

定义:符合1NF的关系中的每个属性都不可再分。

以手机商品为例:

不符合示例

型号参数
手机1白色、16g
手机2黑色、16g
手机3金色、32g
参数 还能继续拆分成 颜色内存两个属性,所以不符合1NF。

符合示例

型号参数-颜色参数-内存
手机1白色16g
手机2黑色16g
手机3金色32g

1NF是最基本的设计规范,但是优缺点也很明显。 优点:查找高效,只用查一个表就可以查到想要的数据。 缺点:数据冗余,插入异常,删除异常,修改异常。

二范式(2NF)

定义:在1NF的基础之上,消除了非主属性对于码的部分函数依赖。

以客户和联系人关系为例:

不符合示例

公司名称公司邮箱公司地址联系人名称联系人手机号
公司1gongsi1@163.comxx1省xx1市xx1区老板1111111
公司2gongsi2@163.comxx2省xx2市xx2区老板2222222
公司3gongsi3@163.comxx3省xx3市xx3区老板3333333

码是(公司, 联系人手机号),从 公司 可以获取到 公司邮箱公司地址, 从 联系人手机号 可以获取到 联系人名称。这个表中存在部分函数依赖,所以不符合2NF。

符合示例

公司名称联系人手机号
公司1111111
公司2222222
公司3333333
公司名称公司邮箱公司地址
公司1gongsi1@163.comxx1省xx1市xx1区
公司2gongsi2@163.comxx2省xx2市xx2区
公司3gongsi3@163.comxx3省xx3市xx3区
联系人名称联系人手机号
老板1111111
老板2222222
老板3333333

为了建立公司和联系人的关联关系,我们又拆出了客户表和联系人表,主表只存放关联关系。

三范式(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。