最近在做一个海报的项目,需要那种所见即所得的编辑方式。框架的话用的是vue。我的设计是使用json
串来存储页面的海报样式,这样提交给后台的就是一个json
字符串,分发下去的时候别的地方再编辑就是先解析json
串,然后再修改json
对象。 以商品模块为例,我们定义出以下的数据结构:
goods: {
imgPath: [],
nameFontFamily: '',
nameFontSize: '',
nameFontColor: '#666666',
fontSize: '',
fontFamily: '',
fontColor: '#666666',
goodsArr: [{
goodsImg: '',
goodsName: '',
goodsDsc: '',
goodsPrice: ''
}]
}
这里也就涉及到不同的前端工程中必须保证json
对象的结构的一致性。那么问题来了,假设我有三个工程都有需要解析和编辑这个json
数据的地方。假设需求变了json
数据变了,如何能够有效的保证这几个的一致性呢?
数据容器的结构的一致性
java
中我们常见到的是声明一个vo
然后多个工程应用同一个vo
这样的话就不会乱了。
那么参照后台的代码我们可以怎么优化我们的代码结构呢?
首先我们在私服库上上传我们自己的一个node
工程chaos-dto
,这个工程专门用来存储一些公用的数据传输对象。
然后我们在里面按照业务需求分包,在建一个PosterGoodsDto.js
文件。
class PosterGoodsDto{
constructor() {
this.goodsImg = '';
this.goodsName = '';
this.goodsDsc = '';
this.goodsPrice = '';
}
}
export default PosterGoodsDto;
由于javascript
中没有私有成员的这一说法,因此我们也就不必多此一举的为每个属性提供一个getter
和setter
了。不过ES5
之后javascript
的对象属性就自带getter
和setter
。如果我们想要在获取或者设置属性的时候做一些事情的话,可以使用Object.defineProperty
方法来设置。但是不是很建议,虽然vue就是借此完成双向绑定的,但是还是觉得这种做法有点“脏”,也就是说很多时候你在这里增加了,有可能会给别人带来副作用(个人见解,虽然一年前,我写java的时候还这么干过)。
然后我们在其他的工程上面就可以从私服库中引入这个工程了。在有变更的时候,其他的工程只需要更新这个npm
包即可。于是我们的代码变成这样:
import PosterGoodsDto from 'chaos-dto/src/poster/PosterGoodsDto';
goods: {
imgPath: [],
nameFontFamily: '',
nameFontSize: '',
nameFontColor: '#666666',
fontSize: '',
fontFamily: '',
fontColor: '#666666',
goodsArr: [new PosterGoodsDto()]
}
如何赋值
当我们该用上面的对象的写法的时候,就面临着我们的赋值就不能单纯的使用data = JSON.parse(data)
来赋值了。
这种等于号的赋值有两个问题,第一是当数据结构有变更时,老的json
串会覆盖掉新的对象,包括新增的属性;第二,就算没有结构上的变更,这里直接等于号赋值,所得到的值也不是原有对象的实例了。
乍一看第一个问题比较严重,但是也很好解决,因为有很多现成的库都实现了对象的深合并的功能,类似于merge
。我们可以利用这些现成的库函数来让老数据不至于覆盖掉新加入的属性。
但是第二个也不能忽略。
假设我们有这样一个场景,我需要对这个容器的数据进行校验。很显然这种字段的校验不宜和这个数据对象分开。比较理想的情况是,每个数据对象提供自己的属性有效性校验函数valid
,调用的地方只需要调用valid
方法,即可实现对于数据的校验。于是我们对对象做了如下的修改。
class ValidResult {
constructor() {
this.status = true;
this.message = '';
}
error(message) {
this.status = false;
this.message = message;
}
success(){
this.status = true;
this.message = '校验通过';
}
}
class PosterGoodsDto{
constructor() {
this.goodsImg = '';
this.goodsName = '';
this.goodsDsc = '';
this.goodsPrice = '';
}
valid() {
var validrst = new ValidResult();
if(!this.goodsImg) {
validrst.error('商品图片不能为空');
} else if(!this.goodsName) {
validrst.error('商品名称不能为空');
} else if(!this.goodsDsc) {
validrst.error('商品描述不能为空');
} else if(!this.goodsPrice) {
validrst.error('商品价格不能为空');
} else {
validrst.success();
}
return validrst;
}
}
export default PosterGoodsDto;
注意,这里的PosterGoodsDto
是一个真正意义上的对象了,有属性有方法。如果我们使用merge
来合并对象,然后在赋值的话,就会丢失这个方法。这个也很容易想到,因为虽然数据结构上一模一样,但是实际上已经是两个完全不同的对象了。这里我们就需要类似于java
中自动注入的那种方式来赋值了。其实实现起来也不难,就是深度遍历对象,然后取对应的值再赋值。
总结
或许我这里说到的东西早就有人实现了吧。但是个人觉得javascript
在写这种代码的时候还是显得很凌乱,效率也不高(因为很多时候由于编辑器的提示不够好,我们不得不对着原对象的代码,来写出属性的名字)。
以前我很喜欢javascript
的弱类型的特质给我带来的便利,但是目前看来也是深受其害,算是一把双刃剑吧。这两天也在看typescript
的文档,觉的typescript
未来很有可能会成为前端开发的主流,相信也会有一大波围绕着typescript
的类似于spring
的框架的出现和普及。