Fork me on GitHub

怎么写好组件

我们为什么要写组件呢?这里不细分组件、插件、控件,追究其原因无非让代码,能够复用,追求更快的开发效率。其实还有个重要的原因,项目大了之后,难以维护。这个时候就会把项目中重复的部分抽取出来,形成一个组件。但是组件也会有些’缺点’,这个最后讲。

组件需求

要实现如图的一个条件选择器
enter description here

有的时候,项目时间紧张,就会直接切图,通过jquery的dom选择器实现这个’简单的功能’。

需求分析

为了更好的维护,以及更好的复用此组件,就要做些抽象。

数据层: 用来决定按钮个数以及按钮是否选择

表现层: 按钮使用现有的ui组件

逻辑层: 按钮事件等逻辑处理

数据层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
data: null,
choseT: 0,
choseF: 0,
getDataStatistics: function() {
var list = this.data;
var choseT = 0;
var choseF = 0;
var len = list.length;
for (var i = 0; i < list.length; i++) {
if(list[i].checked == 'checked') {
choseT++;
}else {
choseF++;
}
}
return {
choseT: choseT,
choseF: choseF,
len: len
};
},
dataChangeAll: function(checked) {
var list = this.data;
var len = list.length;
checked = checked || '';
if(checked == 'checked') {
this.choseT = len;
this.choseF = 0;
}else {
this.choseT = 0;
this.choseF = len;
}

for (var i = 0; i < len; i++) {
list[i].checked = checked;
}
},
dataChangeSingle: function(index, checked) {
var list = this.data;
var choseT = this.choseT;
var choseF = this.choseF;
if(checked == 'checked') {
choseT++;
choseF--;
}else {
choseT--;
choseF++;
}
this.choseT = choseT;
this.choseF = choseF;
list[index].checked = checked;
}

数据层主要对原始数据做些CURD的一些操作,具体的操作看具体的业务需求,但是要具有这个意识。

表现层

说白了表现层也就是template层或者view层,就是用户所看到的,一般会用一个比较成熟的ui库,比如bootstrap。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
getHtml: function(list, statistic) {
var html =
['<div class="sales-dialog">',
'<div>',
'<div class="tag-box-chose J_view_checkNav">',
this.getChoseNav(statistic),
'</div>',
'<div class="mt_10 J_view_checkItems">',
this.getChoseItem(list),
'</div>',
'</div>',
'</div>'].join('');
return html;
},
getChoseNav: function(statistic) {
var checkAll = '';
var checkNone = '';
var len = statistic.len;
if(statistic.choseT == len) {
checkAll = 'checked';
}
if(statistic.choseF == len) {
checkNone = 'checked';
}
return [
'<label class="tag '+checkAll+'">',
'<input type="radio" name="radio-grade input-fat" class="J_view_checkAll" '+checkAll+' />',
'<span class="tag-tit">全选</span>',
'</label>',
'<label class="tag '+checkNone+'">',
'<input type="radio" name="radio-grade" class="J_view_checkNone" '+checkNone+'/>',
'<span class="tag-tit">全不选</span>',
'</label>'
].join('');
},
getChoseItem: function(list) {
var inputs = '';
var doInputFunc = function(i, detail) {
return [
'<div style="display:inline-block;width:150px;">',
'<label data-toggle="checkbox" class="checkbox-pretty inline '+detail.checked+'">',
'<input type="checkbox" value="'+i+'" class="J_view_checkItem " '+detail.checked+'><span>'+detail.name+'</span>',
'</label>',
'</div>',
].join('');
};

for (var i = 0; i < list.length; i++) {
inputs += doInputFunc(i, list[i]);
}
return inputs;
},
domAction: function($el, type, data) {
var html = '';
type = type || 'all';
if(type == 'item') {
html = this.getChoseItem(data);
$el.find('.J_view_checkItems').html(html);
}else if(type == 'all') {
html = this.getChoseNav(data);
$el.find('.J_view_checkNav').html(html);
}
}

众所周知,template就是根据数据渲染成html,在spa项目尤其重要。

enter description here

逻辑层

这层主要做 调用template方法将数据渲染到页面上;将页面上的一些事件结果,映射到数据层。其实现在流行的MVVM模式,就是在逻辑层这里做了更多的事情,只是开发者们不用去关心细节处理,更专注业务的开发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
eventBind: function($el) {
var _this = this;
var $target = '';

// 全选
$el.on('change', '.J_view_checkAll', function() {
if(!$(this).parent().hasClass('checked')) {
_this.module.dataChangeAll('checked');
_this.view.domAction($el, 'all', _this.module.getDataStatistics());
_this.view.domAction($el, 'item', _this.module.data);
}
});

// 全不选
$el.on('change', '.J_view_checkNone', function() {
if(!$(this).parent().hasClass('checked')) {
_this.module.dataChangeAll('');
_this.view.domAction($el, 'all', _this.module.getDataStatistics());
_this.view.domAction($el, 'item', _this.module.data);
}
});

// 单个
$el.on('click', '.J_view_checkItem', function() {
$target = $(this);
var index = $target.val();
var checked = '';
if($target.prop('checked')) {
checked = 'checked';
}
_this.module.dataChangeSingle(index, checked);
_this.view.domAction($el, 'all', _this.module.getDataStatistics());
});
},
eventUnbind: function($el) {
$el.off('change');
$el.off('click');
},

完整案例

总结

这样子写能更好的抽象出公共部分,在其它模块就只要传入数据就可以了,不用重复拷贝代码了。

一开始说到组件会有‘缺点’?尤其是业务组件?

分析

项目版本迭代是一个很正常的事情,第一版的时候,比如这个数据选择项这个组件,在每个模块都有这样的需求。但是在下一个版本的时候,产品经理在其中一个模块更改了业务需求,这就导致这个模块的数据选择项,跟其它模块的数据选择项不一样了,但是又有80%甚至90%的相似度,这个时候就非常困扰,到底是重新写个,还是再对原来的组件,增加个兼容配置项?

重新写会有很多重复的代码。新增配置项,又必须保证之前所有的模块都要正确,得必须都验证过去。有人就会觉得直接去验证好了啊,但是项目大了之后,一个一个去验证不是解决问题的办法。

最终解决

在写组件的时候,业务逻辑部分,现预留配置项,以便后面业务发生改变,通过配置项来重置。尤其是觉得产品经理会更改频繁的部分。
实在是觉得更改逻辑较大,那就重新写个吧,因为一个一个去验证之前的模块的成本还是很大的。

本文地址 http://xiaoqiang730730.github.io/2016/07/16/怎么写好组件/

觉得有点意思,打个赏鼓励博主继续写哈!がんばって
前端-小强 WeChat Pay

微信打赏

前端-小强 Alipay

支付宝打赏