共计 5632 个字符,预计需要花费 15 分钟才能阅读完成。
简介: 数据模型对于惯例的数据查问或填写数据提交,是否有应用场景或者价值?数据模型这条路走的是否有问题?
一、前言
Vmo 是我在 18 年公布的一个工具库,用于疾速创立数据模型,过后我写了一篇文章《Vmo 前端数据模型设计》失去过一段时间的关注,过后我从事三维装修相干的我的项目。在图形学的背景根底及海量简单的数据的状况下,自然而然在前端则会衍生出一种数据处理、解析、生产的技术计划,也种下了我对数据模型概念的种子。
简略举个例子:须要解析一个三维装修的房子的数据会有哪些呢?
- 房子(House), 楼层(Layer), 房间(Room), 墙体(Wall), 墙面(WallSpace), 墙角(Corner), 吊顶(Ceiling),踢脚线(Skirting),地(Floor,带厚度),高空(FloorSpace),门(Door),窗(Window)。
- 以及会延长进去大量的变体,比方飘窗,直角飘窗,弧形窗,墙洞,楼梯等等。
在解析这些数据中存在十分多的互相关联和计算,比方 房间须要和墙面,墙面须要和墙体关联,墙体和最多 2 个房间关联,墙角和多个房间关联,墙角和多个墙体关联等等
面对这样海量、简单的数据,如果只靠着一个 API 申请的后果生产显然是十分不可取的计划,先不说这些数据能不能正确的解析进去,就说这些数据如何保护,保留时如何收集到所有数据反向序列化给后端都是些头疼的问题。
当然这些问题在过后咱们形象的各个数据模型中失去了解决,如果想理解具体细节能够查看我之前的文章。
明天我想讲的是,在我退出阿里后,始终在思考的对于数据模型的两个问题:
- 是不是数据模型这种事件对于惯例我的项目没有应用场景或者价值呢?惯例的,像一些数据查问,或者填写一些数据提交。这种需要外面有必要应用什么抽象类,什么数据模型吗?
- 为什么在前端圈子外面,很少有看到这方面的内容,当初前端圈子里大多都是在走向函数化,Composition 等等,是不是这条路子走的有问题?
在寻找了 2 年后,主导 Lazada 商家端的商品发布页面重构时,好像找到了一些答案。
二、商品模型
首先在新增一个商品的过程中,实际上是用户在以客户端的模式制作一组商品数据,惯例的前端视角来看就是提交一份“JSON”。
而编辑就是通过 API 拉取这份“JSON”解析到 Form 表单中,让用户进行编辑后,再将这份“JSON”提交。
那么粗略的将数据抽象为模型将会成为这样:
Well,到目前为止,咱们做的事件都感觉像是在脱裤子放屁,多此一举。哈哈哈,各位看官暂且勿喷,稍安勿躁。
那么为什么须要把这些数据抽象为一个类呢?我拿一下几个 Case 来阐明:
1、申请数据 & 单元测试
很多时候,前端把对数据的申请和解决是写在组件中的,更优一点可能会封装在某个聚类外面,或者某个 Hook 外面,调用时笨重的拿到状态和数据。
像商品这样的数据申请形式会存在多种:草稿中获取,编辑中获取,某个类目中获取(不同类目下,商品属性不同)。
每种获取形式申请的接口和参数组合形式可能不同,但最初前端生产的产物却是雷同的。依照策略模式来说,对于一个商品模型的获取只是应用了不同的策略,但产物却是统一的,生产端无论调用何种形式,获取到的后果都是牢靠的 Product 模型类。
有教训的前端都晓得,很多时候,在一个我的项目在一轮轮的迭代后,咱们的接口数据往往会存在局部数据须要前端做肯定解决或者转换。
面对这样的数据处理,如果放在一个组件或者 Hook 中,是不太适合的,在做单元测试或者数据生产的时候都可能会给咱们带来一些阻力。
在我看来,调试一个数据问题最好的方法,就是写一个单元测试,对单元测试预期的后果进行调试,往往比咱们在浏览器中 Mock 一份数据调试数据更高效,对未来的稳定性也更有帮忙。
安全感,数据生产起来,一个类和一份 JSON 给开发者带来的安全感和爽感是齐全不同的。生产过数据模型 或者 次一点 生产过 Interface 的小伙伴,我置信对这一点是十分认同的。
哈哈,说到这里有些小伙伴可能要问了,你说的这个咱们用 Interface 也能达到同样的成果呀。好,咱们持续 …
2、计算性生产数据
什么叫计算性生产数据的,说的简略点,就比方:
class Person1 {
fistName = "Wang";
lastName = "Yee";
get fullName() {return `${this.lastName} ${this.fistName}`; // Yee Wang
}
get fullNameCN() {return `${this.fistName} ${this.lastName}`; // Wang Yee
}
}
下面这个例子十分经典且清晰,元数据中可能只是些根本数据,然而很多时候前端须要依据不同场景来进行元数据组装,以往这些数据往往会被封装为各个办法,或者被当做 template 写在组件中,散落在各个角落,每当用到这份数据时可能又会从新依照场景组装一遍。往往这种时候就会存在 需要缺失,比方某状况下须要将之前所有生产到 fullName 的中央改为小写。
拿到商品公布来说,计算性生产数据到底有哪些利用场景呢?
在此之前,我想先解释一下 SKU 这个数据模型,它其中最外围的元数据是:
依照上图这个表格中所示,能够看到该商品共有 6 个 SKU,第一个 SKU 所对应的 SKU 模型数据应为:
class SKU {
value = new Map([
[new SKUProperty({ id: 1, label: "Color Family"}),
new SKUValue({id: 101, label: "Red"}),
],
[new SKUProperty({ id: 2, label: "Size"}),
new SKUValue({id: 201, label: "33"}),
],
]);
price: string;
}
像这样一个 SKU Model,它所具备的元数据曾经能够清晰形容以后 SKU,而且能够通过 SKU 的扩大办法做到很多有用的数据,比方:
- getProperties() 获取该 SKU 有所有属性,如:Color Family,Size。
- getValues() 获取该 SKU 所有 Value,如:Red,33。
- isEqual(anotherSKU: SKU): boolean 比拟一个 SKU 是否和以后 SKU 完全相同,这在后续的数据合并中十分有用。
- getValueByPropertyId(id: string) 通过 PropertyId,获取一个 SKUValue。
相比与只是一个 Object 对象来说,数据模型可能带来十分多的数据处理和数据扩大能力,当某种状况下须要生产由该数据产生的计算性生产数据时,能够很轻易的进行扩大应用,对于数据结构也有更好的预期和掌控力。
联合对该数据模型的单元测试,就能够清晰疾速的开发数据层,当数据层牢靠后,在视图层生产就会变得行云流水,得心应手了。
举个单元测试的例子:
it("alias sku equal", () => {
const data = [
{
text: "300MB",
value: 2988,
name: "p-1",
},
{
text: "Blue",
value: 2888,
alias: "Blue1",
name: "p-2",
},
];
const sku = SKU.fromData(data);
expect(
sku.isEqual(
SKU.fromData([
{
text: "300MB",
value: 2988,
name: "p-1",
},
{
text: "Blue",
value: 2888,
alias: "Blue2",
name: "p-2",
},
])
)
).toBeFalsy();});
这种 SKU,是一种类型较为非凡的 SKU,它其中会存在 alias 字段,当有这种字段时,在做 SKU 比对时,岂但要对 SKUProperty,SKUValue 的 ID 做比对,还须要对 alias 字段做比对。
所以依照下面的单测来看,后果应该是 false,因为这两份数据中的 alias 是不同的。没方法,这是一个业务需要。
如果在视图层做数据比对时,应用的是纯数据进行比对,很有可能漏掉这部分逻辑,这就会导致我的项目变得顾此失彼,拆东墙补西墙。
反正,在消费层遇到很多的须要对数据处理或判断时,大能够将这部分能力交给数据模型来解决,由数据模型来保证数据的稳定性。
3、数据关系
应用数据模型,还能够帮你清晰治理数据关系,比方商品和 SKU 之间,SKU 和 SKUProperty,SKUValue 之间的关系。
我举个具体案例:
这是一个商品编辑时组 笛卡尔积(Cartesian product) 的过程,当咱们的 SKU 属性被用户增加或者批改时,将会触发笛卡尔积的从新计算出最新的排列组合后果。
比方当用户新增一个尺码为 35 时,笛卡尔积将会多出两项组合后果。同理,如果当维度减少一列时,比方增加材质维度,将会产生更多 SKU 后果。
以往,前端开发者总会将这部分计算过程封装成为一个数学方法,放在 utils 中随时调用,这看起没什么问题。
如果将这个过程看做是,一个 SKUCollection 数据模型的构建过程的话,所有就会将变得牵强附会:
test('sku calculate whether valid', () => {
const skuCellection = SKUCollection.fromData({
'p-3xxxx': [
{
text: '300MB',
value: 2,
},
{
text: '128GB',
value: 3,
},
],
'p-4xxxx': [
{
text: 'Blue',
value: 3,
},
{
text: 'Red',
value: 15,
},
{
text: 'Green',
value: 1,
},
],
});
expect(skuCellection.value).toEqual(// 6 SKU Model);
});
有了这样一个数据模型构造后,就能够清晰的通过数据模型来调用其相干的数据和计算性数据。
另外,不同的数据模型尽管相互依赖,但对数据解析和计算性数据缺互相独立,能够做到独立应用和单元测试。
三、异样模型
商品公布实质上是一个较为简单的表单提交页面。因为字段多,交互简单等起因,在产品设计过程中,就曾经将很多字段先拆分为不同模块,来加重用户心理累赘。
比方会存在:根底信息,商品属性,详描,运费等。
在填写过程中,会存在局部 前端校验 + 后端校验 的场景。
在数据提交或者其余数据写入过程中,后端同时会解决字段校验,当后端发现某个字段填写谬误时,服务端将返回错误信息及谬误字段信息。
为了更好的交互体验,前端将会依据返回获取到字段信息,定位到对应的字段地位,显示错误信息并报红,另外还须要依据以后字段判断其所归属的模块进行报错。
还有一种状况是:服务端的第一层校验通过,调用其余商品上游链路时抛出异样,此时上游链路可能曾经失落字段信息,面对这样的异样数据,前端须要展现在表单顶部,并且提供 traceId,以便追踪定位异样。
这样的异样数据,通常解决都须要和后端重复确认不同 Case 的体现状况,有些异样甚至很难呈现一次,咱们在迭代过程中往往会因为一些组件变动或者逻辑变动失落这部分数据生产能力。
就商品公布来说,不言而喻的 ” 保留 ” 的动作是一个须要解决异样的状况,所以咱们会在提交的中央写上很多后端返回异样时的解决逻辑。
当有一天,有另外一个迭代须要写入操作时,同样也会产生异样的状况,这些的异常情况再次解决时又会有很多数据转换和谬误显示的逻辑。
如果收到这份后端返回数据,将他转换为异样数据模型,而后交由视图层生产,这样会让所有异样模型下须要解决的逻辑复用防止交互逻辑失落。
当然,视图层如何更奇妙的生产该数据模型又是另外一个有意思的设计,此处暂且不表,前面我还会写一篇专门介绍商品公布的视图层状态治理设计。
四、总结
在商品公布中,除了上述提到的几个数据模型以外,其实还构建了一些其余类型的数据模型,如:运费模型,商品质量分模型,类目举荐模型等 … 而后由这些多个子模型独特组合成为一个商品的模型。
这样的数据模型在生产起来,开发者其实不会太过关怀到底须要申请什么 API,返回的数据到底是什么样的,他们的返回是否要解决、转换、兼容等问题。
同时,这样高质量的数据模型其实不依赖于视图层的框架,它能够被抽离作为一个独立的包来治理保护,而后在其余页面引入应用,比方商品域可能遇到的:商品治理,商品抉择,运费编辑,商品质量分预览等等 …
回到结尾,我提到的问题:
- 是不是数据模型这种事件对于惯例我的项目没有应用场景或者价值呢?惯例的,像一些数据查问,或者填写一些数据提交。这种需要外面有必要应用什么抽象类,什么数据模型吗?
- 为什么在前端圈子外面,很少有看到这方面的内容,当初前端圈子里大多都是在走向函数化,Composition 等等,是不是这条路子走的有问题?
首先必定的是,在我所应用的过程中,数据模型的确十分清晰,无力,牢固的解决了我所面到的业务问题,所以它是有价值的。
至于和惯例的需要,到底应该用什么好呢?哈哈,这个问题有个比拟无赖的答复,小孩子才思考什么要什么不要,成年人什么都要,没有什么技术是非黑即白的。
Vite 就只能在 Vue 的我的项目外面应用吗?
什么适合用什么,简略的数据查问展现不须要这么精密的数据处理,当然能够间接拿来即用咯,解决业务问题的办法就是好办法!
至于 Composition API,其实在商品公布的重构过程中,根本绝大多数都是应用这种设计思路来实现的,这样的设计的确能让咱们清晰的分辨每个办法是干什么的,是否会影响交互,以及这样的交互是在做什么,每个交互都在一个地位保护和解决,前面我会独自写一篇介绍。
实际过程中发现,数据模型和 Composition API 并不抵触,一个是用来解决数据层,一个是用来解决视图层,它们相辅相成联合一些订阅模式的设计,就会让整个我的项目的划分异样清晰,我非常倡议大家在当前遇到单点我的项目较为简单时可能应用这一套思路来解决业务问题!
作者:开发者小助手_LS
原文链接
本文为阿里云原创内容,未经容许不得转载