淺談CSS模塊化
為什麽要CSS模塊化?
你是否為class命名而感到苦惱?
你是否有怕跟別人使用同樣class名而感到擔憂?
你是否因層級結構不清晰而感到煩躁?
你是否因代碼難以復用而感到不爽?
你是否因為common.css的龐大而感到恐懼?
如果有,恭喜你來對了地方!本文會為您一一解決這些難題!
那麽如何解決CSS命名問題?
我們看一下CSS是怎麽規範的:使用有意義的或通用的ID和class命名。ID和class的命名應反映該元素的功能或使用通用名稱,而不要用抽象的晦澀的命名。反映元素的使用目的是首選;使用通用名稱代表該元素不表特定意義,與其同級元素無異,通常是用於輔助命名;使用功能性或通用的名稱可以更適用於文檔或模版變化的情況。
常用命名(多記多查英文單詞):page、wrap、layout、header(head)、footer(foot、ft)、 content(cont)、menu、nav、main、submain、sidebar(side)、logo、banner、 title(tit)、popo(pop)、icon、note、btn、txt、iblock、window(win)、tips等
註:類型選擇器避免同時使用標簽、ID和class作為定位一個元素選擇器;從性能上考慮也應盡量減少選擇器的層級。
如何構建結構清晰的CSS?
大家都說CSS學和寫都簡單,那麽寫了多年CSS的同學是否有靜下來思考過,自己寫CSS是有較為系統的和具有一定規範的,而不是草率的寫CSS。另外就是自己寫的CSS在團隊中,別的同學是否能看到代碼就知道您寫的是什麽?如果沒有,那不仿看看這裏提到的一些概念與思想,比如:Sass 、SCSS、LESS、BEM、OOCSS、AMCSS等。讓我們一起來深入了解css吧?
-
首先了解一下BEM(我個人比較喜歡的)
BEM的意思就是塊(block)、元素(element)、修飾符(modifier),是由Yandex團隊提出的一種前端命名方法論。這種巧妙的命名方法讓你的CSS類對其他開發者來說更加透明而且更有意義。BEM命名約定更加嚴格,而且包含更多的信息,它們用於一個團隊開發一個耗時的大項目。
命名約定的模式如下:
-
.block{} // 塊即是通常所說的 Web 應用開發中的組件或模塊。每個塊在邏輯上和功能上都是相互獨立的。
-
.block__element{} // 元素是塊中的組成部分。元素不能離開塊來使用。BEM 不推薦在元素中嵌套其他元素。
-
.block--modifier{} // 修飾符用來定義塊或元素的外觀和行為。同樣的塊在應用不同的修飾符之後,會有不同的外觀。
BEM不是一個框架,它只是一種思想
BEM優缺點
優點:BEM 的優點在於所產生的 CSS 類名都只使用一個類別選擇器,可以避免傳統做法中由於多個類別選擇器嵌套帶來的復雜的屬性級聯問題。在 BEM 命名規則中,所有的 CSS 樣式規則都只用一個類別選擇器。因此所有樣式規則的特異性(specificity)都是相同的,也就不存在復雜的優先級問題。這可以簡化屬性值的層疊規則。代碼清單中的命名規則的好處在於每個 CSS 類名都很簡單明了,而且類名的層次關系可以與 DOM 節點的樹型結構相對應。 缺點: CSS 類名會比較長而且復雜。乍看之下,根據 BEM 命名規則產生的 CSS 類名都會很復雜,但實際上在熟悉了命名規則之後,可以很容易理解其含義。
-
我們再看一下OOCSS(面向對象CSS)
OOCSS 表示的是面向對象 CSS(Object Oriented CSS),是一種把面向對象方法學應用到 CSS 代碼組織和管理中的實踐。 OOCSS最關鍵的一點就是:提高他的靈活性和可重用性。這個也是OOCSS最重要的一點。OOCSS主張是通過在基礎組件中添加更多的類,從而擴展基礎組件的CSS規則,從而使CSS有更好的擴展性。
我們有一個容器是頁面page的1/4寬,有一個藍色的背景,1px灰色的邊框,10px的左右邊距,5px的上邊距,10px的下邊距,以前對於這樣一個樣式,我們常常給這個容器創建一個類,並把這些樣式全部加上。像下面這樣。
-
1 // template 2 3 <div class="size1of4"></div> 4 5 // style 6 7 .size1of4 { 8 9 background: blue; 10 11 border: 1px solid #ccc; 12 13 margin: 5px 10px 10px; 14 15 width: 25%; 16 17 }
然而使用oocss的話,我們不這樣做,我把為這個容器創建更多的類,並且每個樣式對應一個類,這樣是為了後面可以重復使用這些組件的樣式,避免重復寫相同的樣式,就拿這個實例來說,我們給這個容器增加下面的類:bgBlue,solidGray,mts,mlm,mrm,mbm
-
1 // template 2 3 <div class="size1of4 bgBlue solidGray mts mlm mrm mbm"></div> 4 5 // style 6 7 .size1of4 {width: 25%;} 8 9 .bgBlue {background:blue} 10 11 .solidGray {border: 1px solid #ccc} 12 13 .mts {margin-top: 5px} 14 15 .mrm {margin-right: 10px} 16 17 .mbm {margin-bottom: 10px} 18 19 .mlm {margin-left: 10px}
OOCSS的優點
-
減少CSS代碼。
-
具有清潔的HTML標記,有語義的類名,邏輯性強的層次關系。
-
語義標記,有助於SEO。
-
更好的頁面優化,更快的加載時間(因為有很多組件重用)。
-
可擴展的標記和CSS樣式,有更多的組件可以放到庫中,而不影響其他的組件。
-
能輕松構造新的頁面布局,或制作新的頁面風格。
OOCSS的缺點
-
OOCSS適合真正的大型網站開發,因為大型網站用到的可重用性組件特別的多,如果運用在小型項目中可能見不到什麽成效。所以用不用OOCSS應該根據你的項目來決定。
-
如果沒用巧妙的使用,創建組件可能對於你來說是一堆沒用的東西,成為一爛攤子,給你的維護帶來意想不到的杯具,說不定還是個維護的噩夢。
-
最好給每一個組件備寫一份說明文檔,有助於調用與維護。
-
AMCSS(屬性模塊)。
屬性模塊或者說AM,其核心就是關於定義命名空間用來寫樣式。通俗的講就是,給一個元素加上屬性,再通過屬性選擇器定位到這個元素。達到避免過多的使用class。
-
1 // template 2 3 <div am- Row ></div> 4 5 <div am- Column = "12"> Full < /div> 6 7 </ div> <div am- Row > <div am- Column = "4"> Thirds </div> 8 9 <div am- Column = "4"> Thirds </div> 10 11 <div am- Column = "4"> Thirds < /div> </ div> 12 13 // style 14 15 [am- Row ] { /* max-width, clearfixes */ } 16 17 [am- Column ~= "1" ] { /* 1/12th width, floated */ } 18 19 [am- Column ~= "2" ] { /* 1/6th width, floated */ } 20 21 [am- Column ~= "3" ] { /* 1/4th width, floated */ } 22 23 [am- Column ~= "4" ] { /* 1/3rd width, floated */ } 24 25 [am- Column ~= "5" ] { /* 5/12th width, floated */ } /* etc */ 26 27 [am- Column ~= "12" ] { /* 100% width, floated */ }
你會註意到第一件事情就是有am-前綴。這也是AM核心部分,確保屬性模塊不會與現有屬性沖突。你可以使用你自己喜歡的任何前綴名,我常使用的是ui-、css-或者其他前綴,但這些示例中使用的是am-前綴。HTML的有效性對你或你的項目來說是非常重要,就類似於使用data-前綴開頭定義的屬性類似。 你可能會註意到的第二件事情就是類似於1、4或12這樣的值,使用類名變得極為麻煩——造成沖突的機會很多。但定義了我們自己的命名空間,實際上將空間變得很小,用於工作中不會造成沖突。為了更好的工作,可以自由選擇最簡明而且有意義的標記。
我們雖然有這麽多的好的方案去解決css的一些難題,但是有沒有一種東西或者工具來代替我們去做這些呢,作為一個程序員我們不喜歡做太麻煩的事情。那麽接下來我們談一談css的構建工具
OK,我們來探索一下webpack是怎麽實現模塊化的。
With :local (without brackets) local mode can be switched on for this selector. :global(.className) can be used to declare an explicit global selector. With :global (without brackets) global mode can be switched on for this selector. webpack會把class分為兩種,一種是local(本地的),一種是global(全局的)。默認導出的都是本地的,但是你可以通過 :global(...)開關來控制導出全局。下面我們看一下栗子。
-
1 // 輸入 2 3 : local (.className) { background: red; } 4 5 : local .className { color: green; } 6 7 : local (.className .subClass) { color: green; } 8 9 : local .className .subClass : global (. global - class -name) { color: blue; } 10 11 // 導出 12 13 ._23_aKvs-b8bW2Vg3fwHozO { background: red; } 14 15 ._23_aKvs-b8bW2Vg3fwHozO { color: green; } 16 17 ._23_aKvs-b8bW2Vg3fwHozO ._13LGdX8RMStbBE9w-t0gZ1 { color: green; } 18 19 ._23_aKvs-b8bW2Vg3fwHozO ._13LGdX8RMStbBE9w-t0gZ1 . global - class -name { color: blue; }
:local(className)被編譯為唯一可識別的標示,:global(className)原樣輸出,當然我們也可以控制導出的格式。配置如下:
-
1 { 2 3 test: /\.css$/ , 4 5 use : [ 6 7 { 8 9 loader: ‘css-loader‘, 10 11 options: { 12 13 modules: true , 14 15 localIdentName: ‘[path][name]__[local]--[hash:base64:5]‘ 16 17 } 18 19 } 20 21 ] 22 23 }
CSS的scoped實現?
現在在各種框架中都會有scoped屬性,使我們的css具有模塊化性質,不會汙染到其他模塊,那麽scoped是如何實現的呢?我們一起來揭開它神秘的面紗吧?
如果你是一個勤奮好學的同學,你一定會發現在HTML的style標簽中有一個scoped屬性。讓我們來一起看一下這個屬性的神奇吧。
一直以來,文檔上的STYLE元素通常都是作用域全局的,選擇器按照全局的CSS優先規則來設置的。要實現局部的選擇需要先選到容器元素,再用後代選擇器來實現。scoped屬性可以讓STYLE元素不再作用於全局,而從當前STYLE元素所在的容器開始選擇後代。
-
1 <div> 2 3 <style scoped > 4 5 span {color:red;} 6 7 </style> 8 9 <span> 我是第1個DIV內的SPAN </span> 10 11 </div> 12 13 <div> 14 15 <style scoped > 16 17 span {color:green;} 18 19 </style> 20 21 <span> 我是第2個DIV內的SPAN </span> 22 23 </div> 24 25 <div> 26 27 <span> 我是第3個DIV內的SPAN </span> 28 29 </div>
結果:
我們可以看見第三個div並沒有被第一及第二個style所感染,也就是說帶有scoped屬性的css是一個獨立的作用域,不會影響到其它模塊!!太好了,那我們以後在style裏面加上scoped屬性就可以完美解決啦慢!BUT,這種方法只有在火狐瀏覽器才生效,其它瀏覽器即使最新的chrome瀏覽器也不支持哦。我@#¥%……
不要急年輕人,我們來看一下vue的代碼,當我們在style中加了scoped屬性後
咦,這不就是我們剛剛講過的AMCSS(屬性模塊)的應用嗎?也就是說vue在編譯的時候,把帶有scoped屬性的的模塊,加上了一個唯一的屬性,然後通過類名+屬性選擇器的方法來實現模塊化!
其實其他框架也是用的類似的方法,我們再看一下,小程序wepy框架的實現吧?
這個是我們剛才講過OOCSS(面對對象CSS)!!
對的,這樣結合框架來講是不是能夠更能深刻理解我們剛才講過的內容了啊?
如何按需加載css?
有時候我們需要把一些有用的常見的css放到一個common.css裏面,但是當我們項目足夠大的時候,common的內容就會變得異常龐大,而且難以維護。
首先我們不得不說一下當下有幾個比較火的CSS預處理器,Less、Sass 、Stylus和postCss,這是一個CSS史上的巨大飛躍。他主要提供了以下功能
-
嵌套語法
-
變量
-
@import
-
混入
-
繼承
-
函數
-
邏輯控制
了解了css預處理器,那麽我們如何優化我們的common.css呢?
要想解決這個問題,我們首先來看一看LESS中的mixin是如何運作的。
-
1 // 你可以混合“類”選擇器或者“id”選擇器,例如: 2 3 .a, #b { 4 5 color: red; 6 7 } 8 9 .mixin-class 10 11 { 12 13 .a(); 14 15 } 16 17 .mixin-id { 18 19 #b(); 20 21 }
以上將得到:
-
1 .a, #b { 2 3 color: red; 4 5 } 6 7 .mixin-class 8 9 { 10 11 color: red; 12 13 } 14 15 .mixin-id { 16 17 color: red; 18 19 }
小提示:當你調用混合集的時候,括號可加可不加。
-
1 .a(); //這兩種調用方式效果是一樣的 2 3 .a;
如果你想要創建一個混合集,但是卻不想讓它輸出到你的樣式中,你可以在混合集的名字後面加上一個括號。
-
1 .my-mixin { 2 3 color: black; 4 5 } 6 7 .my-other-mixin() { 8 9 background: white; 10 11 } 12 13 .class { 14 15 .my-mixin; 16 17 .my-other-mixin; 18 19 }
輸出:
-
1 .my-mixin { 2 3 color: black; 4 5 } 6 7 .class { 8 9 color: black; 10 11 background: white; 12 13 }
好了,我們知道這個原理就可以用less中的mixins重新修改我們的common.less了,而且不會顯得非常臃腫,我們可以按需加載我們的樣式了,是不是很棒啊
我們的CSS模塊化就講到這裏了,有什麽意見或建議可以聯系我哦!
——————————————————
如果你喜歡我們的文章,關註我們的公眾號和我們互動吧。
淺談CSS模塊化