虽然Bootstrap已经“烂大街”了,但是它至少是个完整的CSS组件库。Bootstrap 文档非常详细,参照示例代码,就能快速上手。
复用Bootstrap所提供的组件样式,有利于原型的制作实现。但原型就是原型,由于HTML页面中充斥着大量无语义的类名,CSS样式就与HTML页面深度耦合了。耦合,使得页面代码既不易阅读,也不易修改。
有时,我们并不只是满足于原型的制作,而是希望得到一个曳光弹。在不舍弃Bootstrap的情况下,需要找到一个既能复用组件,又能减少耦合的办法。
从文档着手
在Bootstrap 文档中,整个框架按层次被划分为四部分:Layout、Content、Components、Utilities。
Layout提供了基于Flexbox的响应式“网格布局”,其中的“列”和“槽”是无语义的类名;Content提供了文字、图片、代码与表格的排版样式,但大部分都是无语义的展示性类名;Components提供了一系列通用性组件,如:面包屑、手风琴、轮播等; Utilities中大都是与元素的盒模型、外观有关的展示性类名。
单个元素的盒模型、排版与外观都可以在外部的CSS样式表中实现,而Bootstrap的核心则是响应式的“网格布局”与通用性组件。在Layout部分的Grid章节中,Sass mixins小节提供了可定制、语义化“网格布局”的解决方案——Sass。
学习 Sass
既然官方已给出解决方案,那么我们顺着路走就好了。Sass没听说过,那就去查、去学!
Sass并不难学,它只是一种用于描述CSS的DSL而已,可参考Sass 文档、《Sass和Compass设计师指南》、《Sass与Compass实战》。
阅读源码
在GitHub上,可以找到Bootstrap的源码仓库。在Ruby社区的Gem托管——RubyGems,可以获得Bootstrap 源码包。
整个Bootstrap框架源码分为两部分:stylesheets和javascripts,而javascripts与交互式组件有关,暂不考虑。stylesheets是由初始化文件、组件文件、mixins文件夹与 utilities文件夹构成。
网格布局
在自有的.scss文件中,覆盖_variables.scss中的变量可实现Bootstrap的定制。Bootstrap的“网格布局”是由_grid.scss调用mixins目录下_grid.scss、_grid-framework.scss中的混合器实现的。
@import "bootstrap/variables";
@import "bootstrap/mixins/breakpoints";
@import "bootstrap/mixins/grid-framework";
@import "bootstrap/mixins/grid";
@import "bootstrap/grid";
mixins目录下的_grid.scss文件用于生成“容器”、“行”、“列” ,以及列偏移,“槽”是由参数$gutter控制。
@mixin make-container($gutter: $grid-gutter-width) {
width: 100%;
padding-right: $gutter / 2;
padding-left: $gutter / 2;
margin-right: auto;
margin-left: auto;
}
参数$max-widths用于控制“容器”的最大宽度,参数$breakpoints用于设置断点,这些参数可以通过传参或使用默认值来调节。混合器media-breakpoint-up是由mixins目录下的_breakpoints.scss文件提供,用于设置媒体查询。
@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {
@each $breakpoint, $container-max-width in $max-widths {
@include media-breakpoint-up($breakpoint, $breakpoints) {
max-width: $container-max-width;
}
}
}
“列”是通过调用混合器make-col-ready、 make-col、make-col-offset来实现的,flex属性控制列宽,margin-left属性控制列偏移。
@mixin make-col-ready($gutter: $grid-gutter-width) {
position: relative;
// Prevent columns from becoming too narrow when at smaller grid tiers by
// always setting `width: 100%;`. This works because we use `flex` values
// later on to override this initial width.
width: 100%;
padding-right: $gutter / 2;
padding-left: $gutter / 2;
}
@mixin make-col($size, $columns: $grid-columns) {
flex: 0 0 percentage($size / $columns);
// Add a `max-width` to ensure content within each column does not blow out
// the width of the column. Applies to IE10+ and Firefox. Chrome and Safari
// do not appear to require this.
max-width: percentage($size / $columns);
}
@mixin make-col-offset($size, $columns: $grid-columns) {
$num: $size / $columns;
margin-left: if($num == 0, 0, percentage($num));
}
媒体查询
Bootstrap的媒体查询是由mixins目录下的_breakpoints.scss文件实现的,这个文件是整个框架的基石。
media-breakpoint-up、media-breakpoint-down、media-breakpoint-between、media-breakpoint-only四个混合器的$name参数,只接受$breakpoints中的存在的键名。而它们又是基于函数:breakpoint-next、breakpoint-min、breakpoint-max,理解了这3个函数的返回值,也就弄懂了Bootstrap媒体查询的实现方式。
@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
$n: index($breakpoint-names, $name);
@return if($n != null and $n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
}
@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
$min: map-get($breakpoints, $name);
@return if($min != 0, $min, null);
}
@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
$next: breakpoint-next($name, $breakpoints);
@return if($next, breakpoint-min($next, $breakpoints) - .02, null);
}
收工
Bootstrap自身不包含自动添加厂商前缀的功能,它依赖于Autoprefixer(Gem包:autoprefixer-rails),支持的浏览器见browserslist。
使用Autoprefixer的话,就不再依赖于用Compass添加CSS 3特性啦!