不知不覺在前端領域馬上一個年頭就要過去了,然而再看看自己的程式碼,果然夠爛,那麼為什麼程式碼一直沒有用物件導向的思維去寫CSS呢?首先有兩點:一點就是感覺沒必要,還有一點就是難控制。為什麼這麼說呢?作為剛入門的人來說,第一寫的程式碼就少,平時也不會感覺到程式碼有什麼問題,等開發多了,雖然感覺到問題了,但是你還是很難去按照物件導向的思維去寫,因為按照物件導向去思維寫需要你把握全域性觀,更是面向未來程式設計,把握不好,越寫越亂。所以很多新手一直都還是按照程式導向來寫。今天我主要用一些實際的例子講解物件導向的CSS以及JS讓你寫更少的程式碼,讓你越來越懶。這篇文章絕對不是侃侃而談,這些例子都是我實際開發中的問題,寫這篇文章的目的就是讓自己以後寫更好的程式碼,同時分享給大家一起共勉。
CSS懶人篇
寫頁面的時候發現好幾處的按鈕都是這種樣式,於是把這個按鈕的樣式單獨提取出來放著全域性css檔案中
1 2 3 4 5 6 7 8 9 10 11 |
.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
因為這些都是不固定的因素,因此有些是不能共用的。
1 2 3 4 5 6 7 8 9 |
.base-btn { display: block; height: 54px; line-height: 54px; text-align: center; background-color: #14B5A9; color: #fff; font-size: 1.4rem; } |
這樣就好很多了,但還是很有問題的,尤其是命名,嚴重的問題,因為不只是有這一種按鈕,看上面的圖片,是有兩種按鈕樣式的,因此我們命名也得改一下。
1 2 3 4 5 6 7 8 9 10 11 |
.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裡面,除非你有一萬份把握。
1 2 3 4 5 |
.btn-w45-h140{ width: 140px; height: 45px; line-height: 45px; } |
雖然這樣寫下來還算有那麼一點物件導向的樣子,但還是有太多的問題,尤其在命名上,因此我建議還是通過元件化來寫。對於基本樣式還是提取出來,然後寫元件。
如這一塊我們可以把它寫成一個元件。
話說雖然市面上有很多寫元件的框架或者庫,但我還是不太滿意,因為往往專案都沒有必要使用那麼大的框架,只是一點點東西而已,但苦於HTML沒有匯入另外一個HTML的功能,這句話擱在以前是對的,但HTML5已經支援匯入另外一個頁面了,詳情可以搜尋
link import html
但可惜的大部分瀏覽器都不支援,安卓的微信倒是支援,不過IOS不支援,UC也不支援,好吧,還是不能用。於是想起了ES6裡面的模板字串,於是有了下文。
我想把這個tab做成元件,下次用的時候直接匯入就可以使用,先來看看怎麼使用吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<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元件名字一樣。然後引入元件檔案,最後傳遞資料。
效果如下:
元件程式碼:
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 64 65 66 67 |
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的語法,這樣看起來更加方便,之所以還有傳遞一個資料過去是因為名稱啥的可能不一樣,如果說這個元件的內容什麼的都是固定的,那就沒有必要留介面了。不過這雖然解決了一下小問題,但還是不足的,可擴充套件性不怎麼好。
1 2 3 |
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可擴充套件性
一個時間格式化元件引出的學問,後臺給返回一個總毫秒數,我得格式化成這個樣子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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,你如果不瞭解不要緊,因為重點不在這,而是思路。
使用:
1 |
<time class="base-gray">時間:{{ item.time | timeFormat 'ymd' '-'}}</time> |
這裡主要的優點就是它根據你輸入的y,m,d而進行輸出,重點還在於如果你需要新增一個欄位,也不必改變程式碼,只需要在time裡面加一個就好了,其他的都不用動,之所以有這種好處就是因為這裡沒有用判斷,而是用了time[select[i]]
根據使用者的選擇來輸出,當然這段程式碼也並不是完美的,多少還是有些問題,但至少會比你直接寫死的好。
2016.09.28更新
上面說了元件化的缺與失,主要原因就是,沒有完美的東西,但如果不考慮整體元件化,那麼情況就會好很多,下面通過一個例子演示。
我想頁面中有很多這種單選效果吧,但注意千萬不要把整個功能當做一個元件,那樣會有很多問題,我只能要它的一部分,也就是功能!我們把它的單選功能封裝起來。
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 |
<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表示第幾個(可能會有需要),這種封裝可比你直接把所有功能都封裝起來好,直接把所以的都封裝,可擴充套件性必然就很差。
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 |
<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> |
完全不必在乎結構長什麼樣子,這就是可擴充套件性的好處。
寫完後發現,元件化還是有很長一段路要走,因為有太多的不足,雖然有太多不足,但並不代表不重要,不需要,也希望你看完這篇文章有一點小小的啟發。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式