Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

谈谈「数据模型」是如何助力前端开发的 - 张尚金 #6

Open
hoperyy opened this issue Aug 2, 2019 · 0 comments
Open

Comments

@hoperyy
Copy link
Contributor

hoperyy commented Aug 2, 2019

一、定义

数据模型是数据特征的抽象,用来抽象定义一个业务对象。假如现在有一个用户模型,如果要抽象的描述这个用户对象,可以按照如下来定义:

const UserModel = {
    name:{
        type:String,
        property:'name',
        value:'zhangshang'
    },
    age:{
        type:Number,
        property:'age',
        value:26
    }
}

其中,type声明数据的类型,property指明映射路径,value是默认值。这里先有个大概的概念就行,后面我会通过实例来详细展开。

二、动机

前面介绍了数据模型的定义,那和前端开发又有什么关系呢?前端又不需要和数据库打交道,前端开发主要就是拿到数据显示就完了,那为什么需要数据模型呢?它是怎么助力前端开发的呢?我们先来看一下以下几个场景。

场景一

我们在前端开发中,通过ajax请求拿到服务端数据,然后将数据显示在视图上,经常会写如下代码:

如示例,假如我们要显示用户头像,通过取到headUrl的值绑定在src属性上即可。因为是异步加载获取的数据,在最终获取到headUrl的值之前,我们需要先判断cardData.buyerExperienceInfo的存在性,然后才能取值,否则在视图初次渲染之前会报如下错误:

在这种场景下,我们在开发中就不得不写一些防御性的代码,久而久之,项目中类似代码会越来越多,碰到层级深的,防御性代码就会写的越来越恶心。另外还有的就是,如果服务端在这中间某个字段删掉了,那就又得特殊处理了,否则会有一些未知的非空错误报错,这种编码方式会导致前端严重依赖服务端定义的数据结构,非常不利于后期维护。

场景二

平时开发中,我们拿到了服务端返回的数据,有些不是标准格式的,是无法直接在视图上直接使用的,是需要而外格式化处理的,比如我司服务端返回的的价格字段单位统一是,跟时间相关的字段统一是毫秒值,这个时候我们在组件的生命周期内,就不得不而外增加一些对数据处理的逻辑,还有就是这部分处理在很多组件都是公用的,我们就不得不频繁编写类似的代码,数据处理逻辑没有得到复用

场景三

在用户做了一些交互后,需要将一些数据存储到服务端,这个时候我们拿到的数据往往也是非标准的,就比如你要提交个表单,其中有个价格字段,你拿到价格单位可能是百位的,而服务端需要的单位必须是分位的,这个时候在提交数据之前,你又得对这部分数据进行处理,还有就是有些接口的参数是json字符串形式的,可能是多级嵌套的,你还要需要特意构造这样的参数数据格式,导致开发中编写了太多与业务无关的逻辑,随着项目逐渐扩大或者维护人员更迭,项目会越来越不好维护。

三、数据模型

在碰到这么多痛点之后,我就在想如何解决,回顾以上场景,总结下来存在以下几个问题:

  1. 前后端数据结构没有解耦,前端在应对不定的服务端数据结构前提下,需要编写过多的保护性代码,不利于维护的同时,代码健壮性也不高。
  2. 基础数据逻辑处理没有和UI视图解耦,容易阻塞视图渲染,同时,在视图组件上存在太多的基础数据逻辑处理,没有有效复用。

所以,这里我引入了数据模型的概念,那通过数据模型如何解决这类问题呢?下面我将通过两个实际案例来进一步呈现上述场景,以及引入了数据模型之后是如何解决的。

四、案列功能

这个案例使用Vue开发,功能很简单,就是通过ajax请求从服务端拿到数据,然后通过vue视图进行展现,效果如下:

五、常规实现

代码只展示主要功能代码,非完整实现

1.请求数据

created生命周期内,向服务端请求数据。

2.数据处理

获取到数据之后,因为拿到的数据和最终UI上显示的格式不一致,需要转化一下数据格式。

3.渲染数据

给当前Vue实例赋值,然后在template里通过模板语法进行渲染

可以看到常规写法,模板语法里面的写法特别不优雅,各种保护性代码(条件判断)

六、通过数据模型方式处理

1.定义数据模型

首先,我们可以专门建一个名叫model的文件夹,专门用来存放模型,然后定义卡片模型cardModel,其中数据定义格式如下:

  1. type 必填,用来描述该字段的类型,支持String、Number、Date等类型
  2. property 必填,数据路径,对应服务端数据结构的取值路径
  3. value 选填,数据默认值,可不填

通过new Model()进行初始化,后续只需要通过model.parse(data)或者model.traverse(data)这个两个方法就可以完成正向映射和反向映射的过程。

具体的使用方式可以查看API

2.请求数据

通过axios请求接口,在拿到数据之后,调用parse方法解析数据,在解析的过程中会去做赋值操作以及数据格式化。

3.数据渲染

拿到数据,赋值给vue组件实例后,在template模板里面直接使用我们事先定义好的数据字段,不需要再去写类似a&a.b&a.b.c这样的代码,且不管服务端数据字段如何变化,视图渲染都不受影响,从而实现和服务端数据结构进行解耦。

与此同时,针对类似价格、时间等需要格式化的数据,我们可以直接使用,不需要再去写对应的格式化处理逻辑,从而专注于视图组件渲染处理。

通过引入数据模型,我们可以看到在模板里面引入变量的时候不需要进行各种判断,写法非常优雅,而且健壮性很强,即使服务端某个字段没有返回,我们这里也不会因此存在报错的可能性。且在脚本里面没有了数据格式化处理代码,从而不会因为数据处理逻辑代码可能存在的错误,打断UI的渲染。从而带来的更大好处是,随着项目的不断迭代,数据和视图有着清晰的划分,前端和后端进行了解耦,项目的可维护性得到保证。

4.反向映射

在库里面,还提供了traverse方法,和parse方法类似,区别是traverse是反向数据生成以及格式还原。

七、模型库的原理

最后,我来讲讲这个数据模型库(ducker-model)的实现原理,源码总共不到200行,还是简单的,可以通过这里下载查看,主要实现逻辑如下:

  1. 声明一个名叫Model的类。
  2. 通过new Model(options),传入模型结构,初始化数据模型属性,对外主要使用的是parsetraverse方法,
  3. parse方法的实现过程就是遍历模型数据结构,拿到每个属性的数据路径,然后根据这个路径去取传入的的数据里面的数据,最后给事先定义好的属性赋值,在赋值的过程中,可以根据type格式化一些类似时间、价格类型的数据。
  4. traverse方法刚好和parse相反,同样是遍历数据模型结构,拿到每个属性的数据路径,然后根据这个数据路径去设置一个新对象的值,这期间,反向格式化数据类型,最后返回这个新对象。

八、更进一步

目前这个库还很基础,只支持了一些常规的功能,能做的事情还很多,比如:

  1. 目前每次拿到请求一次数据之后都需要解析一次,那是否有个缓存机制,在数据没有变动的时候,直接从缓存取数据呢,或者可以直接watch这个解析之后的数据,做到数据变动,视图变动呢?
  2. 现有支持的类型还不够多,可以根据具体业务情况增加一些类型,以应对更多场景,提供可扩展的机制。
  3. 插件机制,比如表单处理,我们是否可以在数据模型定义的时候就定义好字段格式,在提交的时候就可以直接进行格式检测,抛出提醒呢?
  4. 目前的操作方式还比较适合纯粹的渲染式组件,如何和复杂的携带业务交互的组件融合也是需要考虑的。
  5. ... ...

文章末尾会提供模型库下载地址,有需要的可以在此基础上进行扩展,欢迎一起完善这个库,另外,案例demo的地址也提供了,欢迎下载学习理解。

九、源码下载

案例:ducker-model-demo

模型库:ducker-model

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant