微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

怎么写好组件

2016-07-30 23:46 伯乐专栏\/小强

(点击上方公众号,可快速关注)

作者:伯乐在线专栏作者 - 前端-小强

链接:http://web.jobbole.com/87164/


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


组件需求


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



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


需求分析


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


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

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

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


数据层


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。


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项目尤其重要。



逻辑层


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


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%的相似度,这个时候就非常困扰,到底是重新写个,还是再对原来的组件,增加个兼容配置项?


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


最终解决


在写组件的时候,业务逻辑部分,现预留配置项,以便后面业务发生改变,通过配置项来重置。尤其是觉得产品经理会更改频繁的部分。


实在是觉得更改逻辑较大,那就重新写个吧,因为一个一个去验证之前的模块的成本还是很大的。


专栏作者简介 ( 点击 → 加入专栏作者 


前端-小强:前端-小强,前端工程师,现就职于杭州光云科技,前端新人,对IT行业的花边新闻也感兴趣。http://xiaoqiang730730.github.io博客内容包括:解决日常问题的小技巧、对一些问题的思考、以及对某些事或者某些活动的思考。


【今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!


 
前端大全 更多文章 不容错过的 10 篇前端技术热文 Chrome 中的这个彩蛋,你知道吗? JS 统治的世界,烤面包机将能运行 JS 了 判断鼠标移入移出元素时的方向 看懂此文,不再困惑于 JS 中的事件设计
猜您喜欢 几个功能强大的分辨率测试网站 谈谈PhxSQL的设计和实现哲学(上) 在 Debian Linux 上安装配置 ISC DHCP 服务器 搞技术的喝啥酒啊!!! 向中国菜刀致敬的中国蚁剑是怎样一种工具?