大型的 CSS 项目中,往往会遇到 CSS 结构混乱、多人协作命名冲突、没有组件化无法复用…等等问题。为了解决这些问题于是引入 CSS 设计模式,用 BEM 来解决命名空间的问题,用 SMACSS 来解决代码分层问题,用 OOCSS 来解决解构复用问题。
BEM
对于 CSS 选择器的命名空间,一般的做法是通过选择器嵌套来约束命名空间,但是这种方式带来了很多问题 – 嵌套层次深不但影响了性能也导致了重度的 DOM 依赖,还有最重要的是命名冲突。
一个优秀的 CSS 命名方案,应该要解决以下问题:
1 | * 具备 NameSpace; |
BEM 的出现解决了上述问题。
BEM 中,B 代表 Block(Component)、E 代表 Element、M 代表 Modify。规定用 __
连接 E,用 --
连接 M,用 -
连接单词和命名前缀。比如:
1 | .m-page |
注:BEM 最多只有三层,不可能存在嵌套的 Element,比如 .m-page__item__anchor--cur
。
SMACSS
SMACSS 是一个比较全面的 CSS 规范,它不但规范了命名空间,还对 CSS 进行了分层。具体到项目中,只参考它的分层。
命名前缀
命名前缀用于说明 UI 类型归属,可以结合 BEM 一同使用,用于说明 Block 的类型。
- g-栅格
简单栅格系统:用户后台 g-hd
、g-fd
、g-bd
、g-sd
、g-mn
,前台和中间页 g-head
、g-body
、g-foot
。
- f-原子类
常用原子类:f-bfc
、f-clearfix
、f-ellipsis
、f-show
、f-hide
、f-cb
…。
定义原子类必须谨慎,功能性的原子类还好(f-clearfix
、f-bfc
),描述性的原子类必须谨慎(比如 f-w100
)。原子类的使用场景是供后端使用的,或者前端 fix issue 时紧急使用。
原子类的缺陷:与 inline style 无异,没有做到结构与样式的分离;容易导致 class 臃肿。
- u-元件
常用元件:u-btn
、u-ipt
、u-select
、u-line
、u-link
…。
利用 OOCSS 来对页面元素进行抽象,是一种非常好的做法,这样可以通过继承,组合出不同的样式。但是也有导致 class 的臃肿的缺点,不过为了前者的实用性,后者的这点缺点也算不了什么。
- m-组件
常用的 UI 组件:m-tab
、m-table
、m-form
、m-page
、m-dialog
、m-btns
、m-select
、m-search-box...
、m-header
、m-footer
…。
UI 的组件化也就是 HTML、CSS 的组件化。模块化、组件化的目的是封装(作用域隔离)、复用,这使得代码简洁易读易维护,按照最小组件化原则,非常有利于项目在快速迭代中的发展。
组件化原则:
1 | * 组件化抽象的第一原则是复用(这也是 m 与 c 最主要的区别); |
自定义组件:
当组件化不满足场景时,可对 modify 进行自定义(组件不满足场景大部分是 modify 不满足):
1 | m-page--center |
- c-模块
整个 UI 都可以被模块化,包括业务,使用 c-xx
来作为命名前缀(c 指代 component,表示模块,而非 custom)。
component 和 module 的区别:component 包含 module,module 是 component 的组成部分,组件一般是可高度复用,可在整个项目或者跨项目通用的,这里的 c 其实指的是不可复用的业务模块(模块 - 组件 = 不可复用的业务模块
),主要目的是封装,不具备 m 一样的复用能力。
1 | c-login # login 不能被复用,所以这里不能用 m-login |
OOCSS
OOCSS 只是一种思想,并没有具体的规范,用 OO 的方式来对 CSS 进行解构抽象,能够达到很好的作用域封装和复用效果。
1 | .u-btn .u-btn--normal .u-btn--gray |