CSS模組化開發
CSS 模組化
一、檔案結構
常見檔案結構
一個專案的CSS最基本結構通常是下面這樣的:
- base.css
- common.css
- pages.css
複雜一點的專案可能是這樣分:
- base.css
- header.css
- footer.css
- sidebar.css
- forms.css
- icons.css
- buttons.css
- dropdown.css
- modals.css
- layout.css
- index.css
- user.css
- admin.css
- pages.css
如果後期不打算合併CSS的,建議儘可能減少 CSS 檔案的數量。
如果要做合併壓縮 CSS 檔案,則可以對CSS 檔案進行適當的組織,這是 CSS 模組化最簡單有效的方法。建議根據專案情況使用上述的兩種或是其他檔案組織方案。
SMACSS
SMACSS 的全稱叫 Scalable and Modular Architecture for CSS。即可擴充套件和模組化的CSS架構。
SMACSS將樣式分成5種類型:Base,Layout,Module,State,Theme,下面說說每一種型別表示什麼:
- Base: 基礎樣式表,定義了基本的樣式,我們平時寫CSS比如reset.css就是屬於基礎樣式表,另外我認為清除浮動,一些動畫也可以歸類為基礎樣式。
- Layout: 佈局樣式,用於實現網頁的基本佈局,搭起整個網頁的基本骨架。
- Module: 網頁中不同的區域有這個不同的功能,這些功能是相對獨立的,我們可以稱其為模組。模組是獨立的,可重用的元件,它們不依賴於佈局元件,可以安全的刪除修改而不影響其他模組。
- State: 狀態樣式,通常和js一起配合使用,表示某個元件或功能不同的狀態,比如選單選中狀態,按鈕不可用狀態等。
- Theme: 主題面板,對於可更換面板的站點來說,這個是很有必要的,分離了結構和面板,根據不同的面板應用不同的樣式檔案。
更多關於 SMACSS 的資訊參見它的官網。
二、命名
命名的重要性
- CSS 不像一般的程式語言,具有作用域和使名空間概念,它的每一條規則都是全域性的,在 CSS 預處理出現之前,這很容易造成命名上的衝突。
- 如果為了減少命名上的衝突,使用很長的名稱,很可能會造成 CSS 檔案異常巨集大。
- 涉及到多人維護同一段 CSS 程式碼時,命名的意義表達,也會影響到維護效率。
CSS 的規則命名通常有以下幾條原則:
- 使用獨一無二的規則
- 使用簡短的命名
- 巢狀層級不宜過深,建議控制在3層以內
獨一無二的規則
最完美的做法是,每條規則只使用一個選擇器,並且每個選擇器的命名唯一(有一些工具可以實現),但是會造成 CSS 檔案過大,得不償失。更好的做法是適當的使用巢狀:
.module_header { background:#fff; font-size:16px; }
.module_header .header_inner { }
.module_header .header_inner .header_logo { float:left; }
簡短的命名
在能表達意思的前提下,使用單詞的縮寫,這是 CSS 的最佳實踐之一。如上面的例子可以改成:
.mod_header { background:#fff; font-size:16px; }
.mod_header .hd_inner { }
.mod_header .hd_logo { float:left; }
相同的模組使用字首限制,這是最常見的做法:
.det_top { padding:.1rem; }
.det_top h2 a { font-size:18px; color:#2db7f5; }
.det_top_right{float:right;}
.det_top_right button{border: 1px solid #C7CACC;padding: 4px 30px;background-color: #fff;border-radius: 3px;outline:none;}
.det_box { margin-bottom:.2rem; background:#ffffff; border:1px solid #e3e5ea; border-radius:4px; padding:0 .1rem; }
.det_box:last-child { margin-bottom: 0; }
.det_box .box_hd { height:.22rem; line-height: .22rem; padding:.1rem .2rem; border-bottom:1px solid #ecedee; overflow:hidden; }
.det_box .hd_tt { float:left; font-size:14px; color:#4f555c; }
.det_box .hd_edit { float:right; font-size:12px; color:#7b8791; }
一來起到名稱空間的作用,二來模組化效果很清晰。
下面是一些常用的模組命名,可適當地使用單詞縮寫:
頭:header
內容:content/container
尾:footer
導航:nav
側欄:sidebar
欄目:column
頁面外圍控制整體佈局寬度:wrapper
左右中:left right center
登入條:loginbar
標誌:logo
廣告:banner
頁面主體:main
熱點:hot
新聞:news
下載:download
子導航:subnav
選單:menu
子選單:submenu
搜尋:search
友情連結:friendlink
頁尾:footer
版權:copyright
滾動:scroll
內容:content
標籤頁:tab
文章列表:list
提示資訊:msg
小技巧:tips
欄目標題:title
加入:joinus
指南:guild
服務:service
註冊:regsiter
狀態:status
投票:vote
合作伙伴:partner
淺巢狀
還是上面的例子:
.module_header { background:#fff; font-size:16px; }
.module_header .header_inner { }
.module_header .header_inner .header_logo { float:left; }
第三條規則的選擇器可以縮減一下,變成:
.module_header .header_logo { float:left; }
因為一般不會有兩個 logo 都放在頭部。
更多關於 CSS 命名,請看這篇文章
在上面“命名的重要性”裡面提到的缺點,可以通過使用名稱縮寫,還有選擇器巢狀,保證每條規則的唯一性。
專案中存在的問題
- class 前面加標籤:
.top_left p span.top_left_num label:first-child{
font-size: 20px;
}
看上面的例子,由於瀏覽器是從右向左解析 CSS 規則的,所以在找到 .top_left_num
後還要去判斷是不是 span, CSS 解析反而是更慢的。一般只有兩種情況下會用到多個選擇器限定:
- 為了提高優先順序
- 為了與同樣選擇器的元素區分,如下面為了區分展示狀態和可編輯狀態:
.det_box .bd_table .val { display: inline-block; padding:6px; line-height:1; border:1px solid transparent; box-sizing:border-box; }
.det_box .bd_table input.val { width:1.8rem; border:1px solid #e3e5ea; border-radius:4px; background:#eee; }
- 選擇器太大:
.top_right{
float:right;
min-width:400px;
margin-top: 25px;
border-left: 1px dashed #e0ddd4;
padding-left: 20px;
}
.top_right li{
display: block;
line-height: 22px;
height: 22px;
}
上面的程式碼很容易出現重名的情況,導致樣式互相影響。
- 原子類與模組樣式混用:
.admin_childpages_header .f_l span{
font-weight: 700;
color: rgb(46,46,46);
padding: 0 1.75rem;
border-right: 2px solid rgb(191,191,191);
cursor: pointer;
font-size: 14px;
}
.admin_childpages_header .f_l span:last-of-type{
border: none;
}
原子類 .f_l
用於控制元素的 float:left;
,一般與其他原子類組合使用。上面的程式碼中,當這個元素不再需要 .f_l
時,所有使用 .f_l
的規則都會受到影響。建議用一個單獨的 class 來控制樣式。
- 模組之間區分不明顯,不知道哪裡的樣式是同一個模組的。
- 模組沒有添加註釋說明
BEM
BEM是Block,Element,Modifier的縮寫。是比較流行的一種 CSS 命名方式。下面分別介紹這三個概念:
- Block:在BEM的理論中,一個網頁是由block組成的,比如頭部是個block,內容是block,logo也是block,一個block可能由幾個子block組成。
- Element:element是block的一部分,具有某種功能,element依賴於block,比如在logo中,img是logo的一個element,在選單中,選單項是選單的一個element
- Modifier:modifier是用來修飾block或者element的,它表示block或者element在外觀或行為上的改變
根據 BEM 原則,CSS 的命名應該是像下面這樣的:
.block {}
.block__element {}
.block--modifier {}
上面的例子展示了一個BEM專案的類結構,下劃線(__)被用來區分元素,而用連字元(--)是用來修飾元素的。再看一個例子:
.product-details {}
.product-details__price {}
.product-details__price--sale {}
最後的修飾位,通常會加入各種標記,大的、小的、紅的、綠的等等。但是這樣的命名,賣相不太好。
SUIT
Suit起源於BEM,但是它對元件名使用駝峰式和連字號把元件從他們的修飾和子孫後代中區分出來:
.u-utility {}
.ComponentName {}
.ComponentName-modifierName {}
.ComponentName-descendantName {}
.ComponentName.is-someState {}
三、模組之間的關係
巢狀
在寫 CSS 的過程中,通常會遇到兩個模組之間相互巢狀,比如說表單裡面有按鈕,按鈕是一個元件,也是一個小的模組。
解耦
其他
原子類
在阿當的《編寫高質量程式碼 Web前段開發修煉之道》這本書中,提倡並提供了一套原子類,相信很多前端開發者都有所瞭解。它提供了一套包含高度複用樣式的 class ,下面擷取一段書中的 base.css 的程式碼:
/*文字排版*/
.f12{font-size:12px;}
.f20{font-size:20px;}
.fb{font-weight:bold}
.fn{font-weight:normal;}
.t2{text-indent:2em;}
.lh150{line-height:150%;}
.unl{text-decoration:underlline;}
.no_unl{text-decoration:none;}
/*定位*/
.tl{text-align:left;}
.tc{text-align:center;}
.tr{text-align:right;}
.bc{margin-left:0;margin-right:0;}
.fl{float:left;display:inline;}
.fr{float:right;display:inline;}
.cb{clear:both;}
.cl{clear:left;}
.cr{clear:rigth;}
.vm{verticle-align:middle;}
.abs-right{position:absolute;right:0}
.zoom{zoom:1;}
.hidden{visiility:hidden;}
.none{display:none;}
/*長度高度*/
.w10{width:10px;}
.w{width:100%}
.h50{width:50px;}
.h{height:100%}
/*邊距*/
.m10{margin:10px;}
.m15{margin:15px;}
.m30{margin:30px;}
.mt5{margin-top:5px;}
.mt10{margin-top:10px;}
.mt15{margin-top:15px;}
基於這些原子類,我們可以在專案中非常自由、靈活地組合使用,好處就是幾乎不怎麼需要你再寫 CSS 程式碼了。但是,這種做法的缺點也是很明顯的:
-
維護困難
試想,當你在很多個地方用了.w100
,如果要將這些寬度改為150px
,怎麼做? -
程式碼冗餘
經常會出現這樣一種情況,在一個元素上新增好多個 class ,導致HTML程式碼很長。如很簡單的一個按鈕:
<a href="#" class="h30 w30 tc f14 unl p10 fl">按鈕</a>
如果是這樣,其實和直接寫行內樣式有什麼區別:
<a href="#" style="width:30px;text-align:center;padding:10px;">按鈕</a>
四、CSS預處理 - SASS
SASS 是一種 CSS 前處理器,用它的語法書寫樣式,再編譯成 CSS 檔案。它自身相容 CSS 語法,也就是說,把你的 CSS 檔案複製到 SCSS 檔案中也可以正常使用。
SASS 使用了很多模組化和麵向物件的思想,使得我們的樣式更加易維護。下面講一下 SASS 的常用特性。
基本用法
變數
宣告變數,所有變數以 $ 開頭:
// Color
$gray-darker: #333;
$gray-dark: #666;
$gray: #999;
$gray-light: #ccc;
// 文字主色
$main-color: #808080;
// 其他顏色
$body-bg: #f6f6f6;
$mod-bg: #fcfcfc; // 有內容模組背景色
// 常用色
$orange: #f90;
$red: #cb0000;
計算
body {
margin: (14px/2);
top: 50px + 100px;
right: $var * 10%;
}
巢狀
div {
h1 {
color: red;
}
}
編譯後:
div h1 {
color: red;
}
獲取父元素:
a {
&:hover { color: #ffb3ff; }
}
使用 @at-root
規則:
.zc_about {
@at-root .zc_about_list {
background-color:$mod-bg;
text-align:center;
overflow:hidden;
}
}
繼承
SASS 可以用 @extend
繼承一個選擇器:
.uc_btn_default,
.uc_btn_orange {
background-color: $orange;
color: #fff;
&:visited {
@extend .uc_btn_orange;
}
}
進階用法
Mixin
Mixin 用於定義一些公用的部分,定義一個 Mixin:
// 兩邊間隙佈局
@mixin layout {
padding-left:10px;
padding-right:10px;
}
通過 @include
使用:
div {
@include layout;
height:1rem;
}
Mixin 還可以指定引數:
// icon基本樣式
@mixin icon-base($size) {
display: inline-block;
width:$size;
height:$size;
vertical-align: middle;
background-repeat: no-repeat;
background-size: contain;
}
顏色處理
lighten(#cc3, 10%) // #d6d65c
darken(#cc3, 10%) // #a3a329
grayscale(#cc3) // #808080
complement(#cc3) // #33c
流程控制
條件判斷:
@if lightness($color) > 30% {
background-color: #000;
} @else {
background-color: #fff;
}
在 CSS Sprite 中使用迴圈語句:
@for $i from 1 through 4 {
.ico#{$i} {
background-position:-25px*($i - 1) 0;
}
}
function()
SASS 還可以編寫函式:
@function double($n) {
@return $n * 2;
}
#sidebar {
width: double(5px);
}
SASS 的檔案結構
由於 SASS 的預處理特性,我們可以把模組單獨放到一個檔案裡面,使用資料夾來分類,更加方便管理,以下是一份比較完整的 SASS 檔案結構組織:
sass/
|
|– base/
| |– _reset.scss # Reset/normalize
| |– _typography.scss # Typography rules
| ... # Etc…
|
|– components/
| |– _buttons.scss # Buttons
| |– _carousel.scss # Carousel
| |– _cover.scss # Cover
| |– _dropdown.scss # Dropdown
| |– _navigation.scss # Navigation
| ... # Etc…
|
|– helpers/
| |– _variables.scss # Sass Variables
| |– _functions.scss # Sass Functions
| |– _mixins.scss # Sass Mixins
| |– _helpers.scss # Class & placeholders helpers
| ... # Etc…
|
|– layout/
| |– _grid.scss # Grid system
| |– _header.scss # Header
| |– _footer.scss # Footer
| |– _sidebar.scss # Sidebar
| |– _forms.scss # Forms
| ... # Etc…
|
|– pages/
| |– _home.scss # Home specific styles
| |– _contact.scss # Contact specific styles
| ... # Etc…
|
|– themes/
| |– _theme.scss # Default theme
| |– _admin.scss # Admin theme
| ... # Etc…
|
|– vendors/
| |– _bootstrap.scss # Bootstrap
| |– _jquery-ui.scss # jQuery UI
| ... # Etc…
|
|
`– main.scss # primary Sass file