怎麼寫好組件

咱們爲何要寫組件呢?這裏不細分組件、插件、控件,追究其緣由無非讓代碼,可以複用,追求更快的開發效率。其實還有個重要的緣由,項目大了以後,難以維護。這個時候就會把項目中重複的部分抽取出來,造成一個組件。可是組件也會有些'缺點',這個最後講。javascript

組件需求

要實現如圖的一個條件選擇器html

enter description here

有的時候,項目時間緊張,就會直接切圖,經過jquery的dom選擇器實現這個'簡單的功能'。java

需求分析

爲了更好的維護,以及更好的複用此組件,就要作些抽象。jquery

  • 數據層:用來決定按鈕個數以及按鈕是否選擇git

  • 表現層:按鈕使用現有的ui組件github

  • 邏輯層:按鈕事件等邏輯處理bootstrap

數據層

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的一些操做,具體的操做看具體的業務需求,可是要具備這個意識。dom

表現層

說白了表現層也就是template層或者view層,就是用戶所看到的,通常會用一個比較成熟的ui庫,好比bootstrap。ui

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項目尤爲重要。this

enter description here

邏輯層

這層主要作 調用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%的類似度,這個時候就很是困擾,究竟是從新寫個,仍是再對原來的組件,增長個兼容配置項?

從新寫會有不少重複的代碼。新增配置項,又必須保證以前全部的模塊都要正確,得必須都驗證過去。有人就會以爲直接去驗證好了啊,可是項目大了以後,一個一個去驗證不是解決問題的辦法。

最終解決

在寫組件的時候,業務邏輯部分,現預留配置項,以便後面業務發生改變,經過配置項來重置。尤爲是以爲產品經理會更改頻繁的部分。

實在是以爲更改邏輯較大,那就從新寫個吧,由於一個一個去驗證以前的模塊的成本仍是很大的。

原文地址 http://tostring.site/2016/07/16/%E6%80%8E%E4%B9%88%E5%86%99%E5%A5%BD%E7%BB%84%E4%BB%B6/

相關文章
相關標籤/搜索