Regular基于属性映射的开发方式

水平有限,算是抛砖引玉吧

本文主要从几个方面展开介绍:
1.什么是基于属性映射的开发方式
2.如何实现属性的映射
3.rwatch在实际开发中的实践
4.对比传统开发方式

什么是基于属性映射的开发方式

页面由许多组件组成,每个组件当前所处的状态其实就是对象的属性值,于是组件间的关系可以看作是属性之间的关系,从而整个页面的功能可以看作是一个属性关系网。

假设有组件A、B、C

1
2
A.b->B.b->C.b
A.a->B.b->C.c

上图虽简单,但就已经包括了所有属性映射的情况。

影响B.b的属性有A.bA.a,多对一。

B.b又会影响到C.bC.c,一对多(多个1对1)。

举个例子

将下拉列表选中的文字,放入搜索框中,搜索框中输入的文字会显示在页面的标题和副标题

dropDown 下拉列表组件
dropDown.selected 当前选中项的索引
dropDown.source 下拉框的所有选项数组

search 搜索框组件
search.value 当前搜索框中的文字

title 页面标题
subTitle 页面副标题

映射关系:

1
2
3
dorpDown.selected + dropDown.source -> search.value
search.value -> title
search.value -> subTitle

生成规则:

1
2
3
search.value = dropDown.source[dorpDown.selected].text
title = search.value
subTitle = search.value

一个完整的功能点

下面是一个接口相关的属性映射关系图

IMG20171208_204625.png-67.8kB

TrendChart

代表接口

params.xxx

接口请求参数

SetChart

获取到响应数据后的操作

data.xxx.xxx

状态

上面所说的映射关系则是从箭头的起点到箭头终点的过程,其中某个属性的改变会影响它所指向属性的改变,开发过程中只需要关注前后属性之间的关系即可,当某个属性发生变动的时候,与它相关的属性就会像一根锁链一样一环扣一环地根据各自的映射关系发生相应的改变。

如何实现属性映射

在这里使用本人开发的rwatch实现属性的映射

rwatch中的属性映射其实是对Regular的$watch方法进行的封装,你可以像下面这样实现上面例子中的属性映射。

dorpDown.selected + dropDown.source -> search.value

1
2
3
4
5
watcher.watch(['dorpDown.selected', 'dropDown.source'], 'search.value', function(sources, target){
var selected = sources[0],
source = sources[1];
return source[selected].text;
});

search.value -> title
search.value -> subTitle

1
2
3
watcher.watch('search.value', ['title', 'subTitle'], function(source, target){
return source;
});

rwatch在实际开发中的实践

由于rwatch基于$watch方法,所以所有的映射规则的执行都是依赖于regular的脏检查机制。

如果你想在事件中同步地获取到通过生成规则改变后的属性是不现实的,所以一切和获取属性相关的操作都应在生成规则中,$watch方法中,或者rwatch提供的then()中。

下面通过一个真实的需求来介绍使用rwatch过程中可能遇到的坑和本人的解决方案

1. 映射关系书写不全面

1
2
3
watcher.watch('dorpDown.selected', 'search.value', function(source){
return this.data.dorpDown.source[source].text;
});

这种代码,当下拉框的列表数据发生改变的时候search.value并不会得到相应的更新,应修改为如代码

1
2
3
4
5
watcher.watch(['dorpDown.selected', 'dropDown.source'], 'search.value', function(sources, target){
var selected = sources[0],
source = sources[1];
return source[selected].text;
});

2.组件的当前属性不支持双向数据绑定

比如上述下拉框的dorpDown.selected这个属性不支持双向数据绑定,则需在事件中手动更新

1
2
3
onSelected: function(e){
this.data.dorpDown.selected = e.selected;
}

3.频繁触发请求

由于页面中所有组件状态的改变都是基于rwatch的,于是获取接口的请求参数在事件中无法获取到组件的最新状态,所以发送请求的行为必须放在$watch中。像下面这样:

1
2
3
4
5
6
7
this.$watch('params.a', function(){
this.request();
});
this.$watch('params.b', function(){
this.request();
});

上述代码解决了发送请求的问题,但是引进了一个新问题:当params.aparams.b同时发生改变时,会发送2次请求,如果params有很多属性同时改变,那么就会发送N次请求,如何解决呢?

利用节流函数

1
2
3
4
5
6
7
request: (function(){
return ut.throttle(function(){
var option = {...};
this.fetchData(option);
}.bind(this), 500);
})();

对比传统开发方式

试着使用传统开发方式完成上面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
onDorpDownSelected: function(e){
var data = this.data,
dropDownSource = data.dropDown.source,
text = dropDownSource[e.selected].text
data.search.value = text;
data.title = text;
data.subtitle = text;
},
onSearchChange: function(e){
var data = this.data;
data.title = e.value;
data.subTitle = e.value;
}

传统开发方式,起点往往是某个事件,专注于处理流程,而不是属性间的映射关系。

个人认为当页面的交互逻辑比较复杂的时候,使用属性映射的方式开发会使得组件的逻辑相较传统开发方式更加清晰、易懂。

不过这种开发方式的缺点也是明显的:

  1. 需要额外通过节流函数避免多余的请求发送
  2. 依赖脏检查机制,不够“及时”
  3. watcher过多,性能问题?

后续会针对rwatch做更加全面详细的讨论

嗯~先抛个砖

0%