文章出處

2016.10.29更新 本文存在大量的錯誤,僅供參考。

不知不覺在前端領域馬上一個年頭就要過去了,然而再看看自己的代碼,果然夠爛,那么為什么代碼一直沒有用面向對象的思維去寫CSS呢?首先有兩點:一點就是感覺沒必要,還有一點就是難控制。為什么這么說呢?作為剛入門的人來說,第一寫的代碼就少,平時也不會感覺到代碼有什么問題,等開發多了,雖然感覺到問題了,但是你還是很難去按照面向對象的思維去寫,因為按照面向對象去思維寫需要你把握全局觀,更是面向未來編程,把握不好,越寫越亂。所以很多新手一直都還是按照面向過程來寫。今天我主要用一些實際的例子講解面向對象的CSS以及JS讓你寫更少的代碼,讓你越來越懶。這篇文章絕對不是侃侃而談,這些例子都是我實際開發中的問題,寫這篇文章的目的就是讓自己以后寫更好的代碼,同時分享給大家一起共勉。

CSS懶人篇

寫頁面的時候發現好幾處的按鈕都是這種樣式,于是把這個按鈕的樣式單獨提取出來放著全局css文件中

.base-btn {
    display: block;
    width: 90%;
    height: 54px;
    line-height: 54px;
    text-align: center;
    background-color: #14B5A9;
    color: #fff;
    font-size: 1.4rem;
    margin: 0 auto;
}

但這絕對是個不正確的做法,還不如不提取,因為寫的太死,這也就是新手為什么不喜歡用面向對象的方式寫代碼的原因,因為新手很難考慮周全,最后反而還不如直接寫的好。看看這個頁面的按鈕。

這里不應該寫width:90%margin:0 auto因為這些都是不固定的因素,因此有些是不能共用的。

.base-btn {
    display: block;
    height: 54px;
    line-height: 54px;
    text-align: center;
    background-color: #14B5A9;
    color: #fff;
    font-size: 1.4rem;
}

這樣就好很多了,但還是很有問題的,尤其是命名,嚴重的問題,因為不只是有這一種按鈕,看上面的圖片,是有兩種按鈕樣式的,因此我們命名也得改一下。

.btn{
    display: block;
    height: 54px;
    line-height: 54px;
    text-align: center;
    font-size: 1.4rem;
}
.btn-14B5A9{
    background-color: #14B5A9;
    color: #fff;
}

我的習慣是用背景顏色命名,主要原因是顏色叫不出名字T_T,當然這種方式還是不同好的,用的時候還得試顏色,如果你有好的命名顏色方法還望能夠分享一下。這里定義了兩個類是很有必要的,一個是基礎樣式,就是說90%以上的按鈕都會有這個樣式就叫它基礎樣式,而下面的.btn-14B5A9是某個特定的按鈕才有的樣式,因此得單獨寫,另外還有寬度和高度,如果頁面大部分都一樣的話,還是可以提取出來寫一個class的,但注意關于寬度和高度是易變的所以千萬不要寫在.btn里面,除非你有一萬份把握。

.btn-w45-h140{
    width: 140px;
    height: 45px;
    line-height: 45px;
}

雖然這樣寫下來還算有那么一點面向對象的樣子,但還是有太多的問題,尤其在命名上,因此我建議還是通過組件化來寫。對于基本樣式還是提取出來,然后寫組件。

如這一塊我們可以把它寫成一個組件。

話說雖然市面上有很多寫組件的框架或者庫,但我還是不太滿意,因為往往項目都沒有必要使用那么大的框架,只是一點點東西而已,但苦于HTML沒有導入另外一個HTML的功能,這句話擱在以前是對的,但HTML5已經支持導入另外一個頁面了,詳情可以搜索link import html但可惜的大部分瀏覽器都不支持,安卓的微信倒是支持,不過IOS不支持,UC也不支持,好吧,還是不能用。于是想起了ES6里面的模板字符串,于是有了下文。

我想把這個tab做成組件,下次用的時候直接導入就可以使用,先來看看怎么使用吧。

<div id="tab"></div>
<script src="tab.js"></script>
<script>
    tab({
        title:[
            'CSS',
            'Javascript',
            'HTML5&&CSS3'
        ],
        content:[
            '這是一篇CSS文章',
            '這是一篇Javascript文章',
            '這是一篇HTML5和CSS3文章'
        ]
    })
</script>

定義一個id,這個id和tab組件名字一樣。然后引入組件文件,最后傳遞數據。

效果如下:

組件代碼:

function tab(obj){
    var html = `
    <nav class="title">
        <a href="#a">${obj.title[0]}</a>
        <a href="#b">${obj.title[1]}</a>
        <a href="#c">${obj.title[2]}</a>
    </nav>
    <ul class="content">
        <li id="a">${obj.content[0]}</li>
        <li id="b">${obj.content[1]}</li>
        <li id="c">${obj.content[2]}</li>
    </ul>
    `;

    var sty = `
    <style>
        body,div,nav,ul,li{
            margin:0;
            padding:0;
        }
        ul{
            list-style:none;
        }
        #tab{
            width:300px;
            margin:100px auto;
        }
        #tab .title a{
            float:left;
            width:33.333333333%;
            height:35px;
            line-height:35px;
            text-align:center;
            border:1px solid #dedede;
            box-sizing:border-box;
            text-decoration:none;
        }
        #tab .title a:nth-last-of-type(-n+2){
            border-left:none;
        }
        #tab .content{
            clear:both;
            position:relative;
        }
        #tab .content li{
            width:100%;
            height:300px;
            outline:1px solid #dedede;
            background-color:#fff;
            position:absolute;
            left:0;
            top:0;
            z-index:-999;
        }
        
        #tab .content li:first-of-type{
            z-index:2;
        }
        #tab .content li:target{
            z-index:3;
        }
    </style>
    `;

    document.getElementById('tab').innerHTML = html;
    document.getElementsByTagName('head')[0].innerHTML += sty;
}

其實原理和字符串拼接一樣,只不過用了ES6的語法,這樣看起來更加方便,之所以還有傳遞一個數據過去是因為名稱啥的可能不一樣,如果說這個組件的內容什么的都是固定的,那就沒有必要留接口了。不過這雖然解決了一下小問題,但還是不足的,可擴展性不怎么好。

document.getElementById('tab').innerHTML = html;
var oHead = document.getElementsByTagName('head')[0];
oHead.innerHTML = sty + oHead.innerHTML;

把style放在最上面,這樣下面就可以去修改里面的代碼。

還有一些細節的問題,就是組件命名,比如說tab,可能有多種樣式的,就是有不同的tab組件,那究竟是都放在一個文件里面,還是另外再創建一個文件?如果是都放在一個js文件里面,那么命名應該如何去名?既然都是tab就不能名字都一樣,所以這也是我們得解決的問題,我的想法就是按照一個順序,或者說按照效果,或者功能去命名。

這種方式顯然也不太好,命名確實也是一個頭疼的問題,實際上我最不滿意的是HTML代碼結構是寫死了的,如果有些地方和這個組件只是相差一點點,可能我們都得重新寫過,想想這段代碼。

你敢保證都是三條數據?這樣的組件還不如不要,寧愿用字符串拼接。看來這種方式還是不太行,不過有一種情況是可行的,就是對于不太可能改動的組件,可以使用這種方式,對于改動較大的還是別用這種方式寫。

先說一下這里用了ES6的語法所以有些瀏覽器不支持,我們還得借助一些工具將ES6轉換成兼容的代碼。轉換教程看這里將ES6轉換成ES5

2016.09.28更新

經過這兩天的研究,最終的結果很遺憾,目前的組件化還是存在著太多的問題,其中最嚴重的問題就是可擴展性,市面上大部分組件化的結構都是寫死的,如果我需要在里面增加一個元素或者刪除一個元素,都是比較麻煩的。雖然有很多不錯的框架,但它們和我想要的結果還是不太一樣,我需要的是簡單,對于不變的組件,直接引入就行。

對于需要傳遞數據的應該這樣。

但盡管這樣,一旦HTML,CSS,JS混合一起必然組件化可擴展性就不可能完美,類似這一個

我們有必要把這個做成組件嗎?最終考慮是沒有必要,其一如果組件了那么究竟用什么標簽?如果這不是問題,那么這個組件了使用方便嗎?引入一個JS?可擴展性呢?你會發現如果用組件化那么會越來越亂,我的想法是用CSS面向對象的方式寫,至少在可擴展性上比較好,這里一再強調可擴展性是因為,我們之所以想要組件化就是為了方便,不需要重復的寫代碼,但如果說事情不是這樣,那么就沒有必要組件化了。

這種頭部啥的都是一模一樣,也不怎么需要改動的,還是建議用組件化的,也不要引什么框架了,就這么個東西還引的話反而麻煩了。記住組件目的就是:簡單,實用,可擴展。

這篇文章會不斷的更新,有好的想法有補充上來,也希望如果你有不錯的建議,也能分享出來。

JS可擴展性

一個時間格式化組件引出的學問,后臺給返回一個總毫秒數,我得格式化成這個樣子。

Vue.filter('timeFormat',function(value,select,split){
    var date = new Date(value);
    var time = {
        y:date.getFullYear(),
        d:toS(date.getDate()),
        m:toS(date.getMonth()+1)
    };
    function toS(value){
        return value>10?value:'0' + value;
    }
    var txt = '';
    for(var i=0,len=select.length;i<len;i+=1){
        if(i==len-1){
            txt+= time[select[i]];
        }else{
            txt+= time[select[i]] + split;
        }
        
    }
    return txt;
})

這里用了Vue.js,你如果不了解不要緊,因為重點不在這,而是思路。

使用:

<time class="base-gray">時間:{{ item.time | timeFormat 'ymd' '-'}}</time>

這里主要的優點就是它根據你輸入的y,m,d而進行輸出,重點還在于如果你需要新增一個字段,也不必改變代碼,只需要在time里面加一個就好了,其他的都不用動,之所以有這種好處就是因為這里沒有用判斷,而是用了time[select[i]]根據用戶的選擇來輸出,當然這段代碼也并不是完美的,多少還是有些問題,但至少會比你直接寫死的好。

2016.09.28更新

上面說了組件化的缺與失,主要原因就是,沒有完美的東西,但如果不考慮整體組件化,那么情況就會好很多,下面通過一個例子演示。

我想頁面中有很多這種單選效果吧,但注意千萬不要把整個功能當做一個組件,那樣會有很多問題,我只能要它的一部分,也就是功能!我們把它的單選功能封裝起來。

<style>
    span{
        float:left;
        width:100px;
        height:35px;
        line-height:35px;
        text-align:center;
        border:1px solid #ccc;
        cursor:pointer;
    }
</style>

<span id="radius1">111</span>
<span id="radius2">222</span>
<script>
    function Radiu(radius1,radius2){
        this.n1 = document.querySelector(radius1);
        this.n2 = document.querySelector(radius2);
    }
    Radiu.prototype = {
        init:function(callback){
            this.click(callback);
        },
        click:function(callback){
            this.n1.onclick = function(){
                callback(1,this.n1,this.n2);
            }.bind(this);
            this.n2.onclick = function(){
                callback(2,this.n2,this.n1);
            }.bind(this);
        }
    };
    new Radiu('#radius1','#radius2').init(function(num,currentEl,siblingEl){
        currentEl.style.color = 'red';
        siblingEl.style.color = '#000';
    });
</script>

這里一個很簡單的封裝,但可擴展性很好,因為只提供一個callback,給一個當前單擊的元素,和一個兄弟元素,以及一個num表示第幾個(可能會有需要),這種封裝可比你直接把所有功能都封裝起來好,直接把所以的都封裝,可擴展性必然就很差。

<div class="method" data-method="2">
    <a href="javascript:;" class="claimMethod_select"><span></span><em>郵寄</em>
</a>
    <a href="javascript:;" class="claimMethod_selected"><span></span><em>自取</
em></a>
</div>
<script>
    function Radiu(radius1,radius2){
        this.n1 = document.querySelector(radius1);
        this.n2 = document.querySelector(radius2);
    }
    Radiu.prototype = {
            init:function(callback){
                this.click(callback);
            },
            click:function(callback){
                this.n1.onclick = function(){
                    callback(1,this.n1,this.n2);
                }.bind(this);
                this.n2.onclick = function(){
                    callback(2,this.n2,this.n1);
                }.bind(this);
            }
    };
    new Radiu('.method>a:nth-of-type(1)','.method>a:nth-of-type(2)').init(
function(num,currentEl,siblingEl){
        currentEl.className = 'claimMethod_selected';
        siblingEl.className = 'claimMethod_select';
    });
</script>

完全不必在乎結構長什么樣子,這就是可擴展性的好處。

寫完后發現,組件化還是有很長一段路要走,因為有太多的不足,雖然有太多不足,但并不代表不重要,不需要,也希望你看完這篇文章有一點小小的啟發。

待更新...


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()