前端数据层初探

大家好,今天我分享的主题是《前端数据层初探》。
这个题目看起来很大,我会试着结合一些例子,试图讲的具体一点。
首先部分同学肯定会有一个疑惑,什么叫数据层?其实大家最近更经常听到的应该是数据流。这些概念其实都比较抽象,我们先脱离前端领域,从实践经验比较丰富的服务端领域来尝试理解。
我选取了我比较熟悉的javaWeb。这是java应用比较常见的一种分层方案。我们大部分人应该都写过一些简单的web服务,我从具体场景来介绍一下这个分层方案。
假设前端发起了一个请求,传上来一个用户的id,要获取这个用户所属的家族的家族成员列表,这时候请求会先到达controller,控制器。
当然数据是存储在数据库的,那我们中间这一层一层的东西是什么?首先是service,由于控制器是直接面向前端的,通常会做一些数据格式的处理、字段筛选的逻辑,面向视图层。因此通常我们不会在控制器里编写具体的面向业务的逻辑。比如这个场景,我们就需要在service中查询用户表,获得用户的家族id,再到家族表中把家族成员的id给查询出来,整合成一个数据返回给controller,所以service也有命名为logic的,通常就是处理具体业务逻辑的层。
Model层,领域模型层是做什么的?这个我们先跳过,来看一下DAO这一层,也就是数据访问层。由于服务端对数据库的访问是比较繁琐的事情,通常还要做一些关系数据库到对象的映射,所以这一层是直接面向数据库的表结构编程的,很机械,比如根据id查询用户数据,根据id更新用户数据之类的逻辑。这一层无法对业务领域进行抽象,所以通常会讲面向某种业务领域的逻辑抽象成领域模型,也就是Model层。
如果区分不了DAO层和Model层,其实可以思考一下,比如我们抽取了一个UserModel,用来处理用户的逻辑,那可能会有一些数据库底层没有的逻辑,比如对用户名进行长度拦截、对用户名进行合法性校验等等业务逻辑。
所以一个请求的链路就完成了,controller获取请求参数、调用service,servies调用多个DAO,DAO查询数据库,然后实例化各种领域模型,处理完数据后返回给Controller层,最后返回一个响应。

因为数据库是天然的单一数据源,请求到响应这样一条链路,几乎没有额外逻辑。 我们通过合适的分层和抽象,就可以让代码非常清晰可维护,也不容易写出bug。

服务端的目的是实现数据业务,那前端呢?
前端不同的地方在于,前端的根本目的是实现人机交互,前端之所以有数据层这个概念,是因为前端还有视图层。

我们理解了什么是数据层了,也就是将业务模型、业务逻辑分层,单独放在一起。那么前端需要数据层吗?

首先这个问题不能一概而论,前端是一个很大的概念,任何实现人机交互的应用都可以称为前端应用。目前主流的类型,我大致可以归纳出这四种。展示性就是业界所说的H5,游戏,业务型是我们最常见的,比如数据分析平台这种实现某类业务的,功能性可以理解成比如 音乐播放器、视频播放器这类应用。

那什么类型的应用最需要数据层?

很容易想到,就是业务型的应用。自动互联网爆发,导致众多传统的业务都开始转型互联网以后,业务型应用的需求就开始激增,这也是前端的三大框架出现的契机,因为业务型应用非常适合数据驱动的方式来开发。

业务型应用开发有什么难点,导致我们需要单独抽取一层数据层?
当然也不是所有业务型应用都具备一样的难度,但是遇到的问题都是非常类似的。
第一点就是数据本身复杂了,通常我们会处理许多来源的数据,如服务端返回的业务数据、程序里配置的数据、为用户交互、表单而创建的数据、一些时间相关的逻辑,还要处理定时器返回的数据、DOM事件,比如路由变化这个最经典的。还有localStorage这些本地缓存、websocket这种实时数据等等。
第二点就是原始数据和展示数据通常具有差异性。
三是程序处理的时序控制,典型的就是竞态问题,比如你正在看一篇文章,但是加载太久了,于是你切换了另一篇。过了一会儿之前第一篇文章数据返回了,如果没有处理好,第一篇的数据可能会把正在看的第二篇文章覆盖掉,导致出bug。
四是数据缓存优化,一些很少变动的数据如果要做缓存,如何优雅的实现也是个可以深究的问题。

这些问题可以用一个简化的业务场景来思考。
有个博客管理后台,这是文章列表,列表上显示这文章的标题和一个删除按钮。删除按钮会根据登录用户的身份是否是管理员,来切换可用和禁用状态。点击删除时,会发起一个请求,删除按钮变为加载状态,删除完成后,从列表上移除这一项。

我们在处理这个业务时,通常做法会是下面这样的流程。

  1. 请求商品的数据和用户的信息
  2. 对返回的数据做一层处理,比如某些值的判空、默认值补充、增加一些业务状态对象,比如为商品增加一个属性 isDeletable,表明是否有权限删除该商品。
  3. 根据用户的信息,设置商品的isDeletable的值。
  4. 由于列表删除时有loading的需求,为每一个列表的数据增加一个isLoading状态,用于绑定删除按钮的loading状态。
  5. 用户点击删除,会请求删除接口,同时将该项的isLoading设置为true
  6. 根据返回值是否删除成功,处理列表数据,重置isLoading状态

这个处理流程,在数据驱动的背景下,应该是非常容易理解的。但是其实上面做的这些步骤,其实是在做很多不同的事情。

  1. 我们请求了业务数据,这里实际上对应的是后端的模型层。
  2. 对业务数据进行了清洗、聚合。所谓清洗,就是讲其中我们不需要的数据去除、将一些边界情况较多的数据做一下兼容处理,比如判空。然后将多个数据进行了聚合,通过用户信息,计算出了商品是否可删除这个业务状态。这个时候已经开始做比后端返回的模型更多的事了。
  3. 针对交互进行建模。根据交互的复杂度,这一步可复杂可简单。我们的例子里就是为数据增加了一个是否正在loading的状态,这一步其实本质上实在针对交互进行建模。
  4. 接收用户交互,这其实是前端应用的本质工作,而所作的针对交互建模,也是为了接受用户交互的。接下来会更新交互模型,请求业务数据。
  5. 最后我们根据业务逻辑,更新本地的业务数据和交互数据,更新视图层。

这个过程在我们开发这类应用中,通常会重复几十上百次。依赖于目前流行的vue、react、angular这些框架,以及配套的数据仓库方案,我们通常可以驾轻就熟的完成这个简单的逻辑。

我们可以参考以网上一张MVVM示例图,来思考这个过程。
vm对象和UI进行声明式绑定,VM通过ajax和后端的model进行通信。VM在这里,其实要做的事情有很多。几乎我们列出的这几个步骤,都是通过VM对象打通的。而这个图中有一个过于理想化的地方,就是model。这个model,它是model吗?根据我们刚才的步骤,前端的model与后端的model之间,至少还差了一层数据清洗和聚合、针对交互进行建模的动作。

即使使用了vuex、redux这类状态管理库,其实他们本质上只是将组件局部的状态提升到了全局罢了,并没有解决以上这几个步骤,这就会导致一些很有趣的现象。有的同学会将store作为model,在action中,做完所有数据清洗和聚合的工作,放到state中的是最终的交互模型对象。这个是做的比较好的行为。而有同学则处于懵懂状态,有时是按照刚才的做法,有时是将store作为原始数据,在组件内取值时,才开始进行数据清洗、聚合的工作。

这是为什么?因为没有范式的指导。即使三大框架都可以很容易的实现我所说的模型层、逻辑层,但是并没有给出一个范式的指导。当然这里面可能是为了考虑保持框架的灵活性,没有去做这么一个限制。

那么我们应该用一个什么方式,才能将以上几个逻辑分层,从而让我们的应用更容易维护和拓展?

不必进行什么颠覆 或者编写什么框架,其实只需要在当前的状态管理和数据驱动之前,加上一层数据层。
数据层中 分为模型层和服务层,服务层负责请求数据,返回业务数据。模型层用来结合业务和交互进行建模。在数据层中,对数据进行清洗、组装、建模后,将模型交给状态管理器。当用户产生交互时,将操作提交到逻辑层,根据业务需要来控制应用状态,提交服务。

基于这样的分层,我们可以做一些更复杂的事,比如对业务透明的数据缓存,和对业务透明的竞态更新之类的复杂逻辑,可以统一在数据层机型一些处理。

接下来我们来回顾一下我们这个分享的目的,首先是我们进行这一系列探索的目的。首先是回顾和总结以往进行业务型应用的开发时,我们常规的做法和一些问题。
然后通过分析,让我们提升大局观,对我们正在编写的逻辑的本质心中有数。
这也为我们后续开发时提供一个方法论,寻找应用的结构中可能存在的优化点,甚至是为重构提供一个方向。

最后总结一下这堂课的内容。
首先是分析了前端应用的数据处理的复杂现状。
然后通过一些案例,分析出当前主流的前端框架在数据层上的缺失。
最好我们给出了一个有落地可能的开发范式。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351