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