CSS 的模組化方法:OOCSS、SMACSS、BEM、CSS Modules、CSS in JS
05 Jun 2018OOCSS、SMACSS、BEM、CSS Modules、CSS in JS … 這些規範(或稱心法、解法)存在的目的都是為了讓程式碼易懂、可重用,進而有效率地開發和維護。
OOCSS(Object Oriented CSS)
兩個原則
- Separation of Structure from Skin:分離結構與樣式。結構像是元素的大小,樣式像是顏色等。
- Separation of Containers and Content:分離 HTML 與 CSS,意即盡量將可共用的樣式提取到單獨的 class 以供使用。
白話文解釋就是「使用 class 撰寫樣式,每個 class 有其各自用途」。
例如
<button class="btn btn-small btn-primary"></button>
其中
btn
:規範按鈕的預設樣式。btn-small
:規範按鈕的大小,在這裡是指小的。可能還有btn-large
(可能是大的)、btn-medium
(可能是中的)。btn-primary
:規範按鈕的顏色,在此「primary」表示暗示使用者作出行動的主色,例如橘色。可能還有btn-default
(可能是白色)、btn-disabled
(可能是灰色)。
這樣的規範讓開發者能設計出一套可經由「組合」而產生多種樣式結構,讓程式碼更精簡、便於管理與維護。範例:Bootstrap。
SMACSS
具結構分類、命名規則的限制。
通則
- 結構分類:Base、Layout、Module、State、Theme。
- 命名規則:id 與 class 受限制地使用、名稱使用 dash 分隔。
結構
- Base:網頁的基本樣式,包含 CSS Reset。
- Layout:將網頁切割成不同區塊(元素),區塊若為唯一出現可用 id,例如
#tab
;若會重複出現則用 class,其中,class 可串連,例如.tab-default.tab-fancy
。 - Module:同 Layout,也是頁面上的區塊,但比較像是區塊的內容,只能使用 class 命名,並且使用 dash 分隔,例如
.tab-item
。 - State:描述元件的狀態,例如:
tab-item active
的active
。 - Theme:針對網站主視覺而定義的 Layout 或 Module 的樣式。 例如
.tab-dark
。
優點同 OOCSS,缺點是結構的分類存在模糊界線。
BEM
BEM 是一種 CSS class 命名的設計模式,將介面切割成許多獨立的區塊,以區塊(Block)、元素(Element)和修飾子(Modifier)來命名,優點是以元件觀念進行開發,具有重用性。它擁有 OOCSS 的架構清楚的美好,也沒有 SMACSS 複雜或令人混淆的部份。另外,由於 BEM 是功能導向的,因此不會像是 OOCSS 或 SMASS 可能會出現為了區別樣式而產生像是 .mt-15
(翻譯:margin-top: 15px
)這種讓人難以理解的 class 名稱。因此 BEM 是一個很優秀的 CSS 架構指南。可參考這裡。
CSS Modules / Scoped CSS
解決全域污染的問題,解法是讓樣式變成限定範圍的。也就是在 class name 加上 hash 就可以產生唯一的名稱,而限定在某個元件上。可參考這裡和那裡。可惜的是,W3C Working Draft 不會將這個功能實現了-[css-scoping] scoped attribute on style element removed from HTML。
在專案的實際應用上,可以使用 CSS Modules、React.js 可用 react-css-modules 或 babel-plugin-react-css-modules,至於 Vue.js 的元件有 <style scoped>
可用。
CSS in JS
當紅代表 Styled Components - 在 JS 中撰寫 inline-style,每個元件只管自己的樣式即可。也就是說,Styled Components 將樣式也併入元件了,搭配著 React.js 可真是元件化到極致了呢。
CSS in JS 的好處是
- 沒有全域污染的問題。
- 共享變數和可用程式邏輯撰寫,就像過去 LESS、SASS 或 SCSS 那樣的便利。
- 每個元件只管自己的樣式,又由於是撰寫 inline-style,不會有冗余(廢棄)的程式碼,只要寫了都是有用的。
- 在 Code Splitting 上由於和元件合併了,因此只會載入要用到的程式碼,增進效能。
缺點就是和框架綁在一起,以後要走其他的路就困難了。
範例。
import React, { Component } from 'react';
import styled from 'styled-components';
const Container = styled.section`
border-radius: 5px;
background: #bbb;
color: #402726;
`;
const List = styled.ul`
color: #fff;
padding: 5px;
`;
const Item = styled.li`
display: ${ props => props.isShow ? 'block' : 'none' }
`;
class Newsticker extends Component {
// 省略不必要的部份...
render() {
return (
<Container>
<List>
<Item key="1" isShow={true}>Hello World!</Item>
<Item key="2" isShow={false}>How are you?</Item>
<Item key="3" isShow={false}>Happy :)</Item>
</List>
</Container>
);
}
}
export default Newsticker;
詳情請見此。
感想
- OOCSS、SMACSS、BEM 三者我較偏愛 BEM,因為規則相對清楚簡單又能達到模組化的效果。
我記不住這麼多東西! - 使用 CSS in JS,尤其是 Styled Components,就代表產品與框架(例如 React.js)綁定了,以後要更換框架等都是不容易的事。但好處就是若產品尚未定型,常常東改西改的,使用 CSS in JS 就可縮小查找和修改範圍,是不錯的開發方式。另外,起初對於動畫、繼承和第三方套件的整合是有疑慮的,解法可參考這裡。