挑逗Bootstrap4源代码 - Grid篇(下)
本文所引用的版本为Bootstrap 4 Beta版,阅读者请先下载好相关源文件。
时光荏苒,若后续版本代码发生变化,将看心情进行更新补充。如果你觉得本文不错,欢迎评论支持,如需转载请标明作者及出处,谢谢。
在日常使用Bootstrap的时候,我们最常见的做法是给HTML内的元素添加上预设的类名,这种方法直观且易于调试。但是对于一个前端洁癖患者来说,在HTML标签内添加一大堆类名简直和内联style一样让人深恶痛绝。那么在这种时候,学会使用Bootstrap的Scss,利用其内置的函数和@mixin
来对你自己命名的类进行样式添加就成了一件很棒很酷的事。
涉及文件
- 变量:_variables.scss(起始行:171,结束行:205)
- 函数:_function.scss //其中函数主要用于变量文件中,在此不述
- 公共类:_flex.scss //在utilities文件夹下,用于flex布局的各种类,只是给属性加了包装,同样不述
@mixin:
- _breakpoints.scss //断点函数区,包括断点区间查找、自动扩展媒体查询等功能
- _grid.scss //辅助mixin,提供容器、行、列创建
- _grid-framworks.scss //核心mixin,依据断点,循环创建以flex为基础的12网格
- 引用:_grid.scss //自动创建包括12列网格在内的布局,本质上是对_grid-frameworks和_grid的引用
- _grid-frameworks.scss
@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) { // Common properties for all breakpoints %grid-column { position: relative; width: 100%; min-height: 1px; // Prevent columns from collapsing when empty padding-right: ($gutter / 2); padding-left: ($gutter / 2); } @each $breakpoint in map-keys($breakpoints) { $infix: breakpoint-infix($breakpoint, $breakpoints); @for $i from 1 through $columns { .col#{$infix}-#{$i} { @extend %grid-column; } } .col#{$infix}, .col#{$infix}-auto { @extend %grid-column; } @include media-breakpoint-up($breakpoint, $breakpoints) { .col#{$infix} { flex-basis: 0; flex-grow: 1; max-width: 100%; } .col#{$infix}-auto { flex: 0 0 auto; width: auto; max-width: none; // Reset earlier grid tiers } @for $i from 1 through $columns { .col#{$infix}-#{$i} { @include make-col($i, $columns); } } @for $i from 1 through $columns { .order#{$infix}-#{$i} { order: $i; } } } } }
整个文件只有一个@mixin make-grid-columns()
,这个@mixin
才是真正的搭建12列Grid网格系统的核心,让我们细细来拆解。
首先是参数,引入了列数($columns
)、列间距($gutter
)、断点列表($breakpoints
)。这三者都已经预设好了,不需要操心。
%grid-column { position: relative; width: 100%; min-height: 1px; padding-right: ($gutter / 2); padding-left: ($gutter / 2); }
下面是占位符%grid-column
,这个在之前讲make-col-ready()
时已经讲过了,出于减小css体积的考虑,占位符在这里显然优于@mixin
。那么这里定义的就是一个列的基本属性,而min-height
的设置也是考虑到当列为空时不至于坍缩。
@each $breakpoint in map-keys($breakpoints) {}
接下来是一个大循环,这个循环的根本目的在于为不同的断点加上相匹配的类,它的循环依据就是断点名。
$infix: breakpoint-infix($breakpoint, $breakpoints);
让我们以“md
”为例进入这个循环,首先是$infix
变量,结合先前的知识,我们了解到,这个$infix
的值将是”-md
”,让我们记住它,接下来,又是一个小循环。
@for $i from 1 through $columns { .col#{$infix}-#{$i} { @extend %grid-column; } }
这个@for
循环的目的,就是为了创建12列的基本属性,因为所有的列都具备%grid-column
定义的基本属性,所以我们将这份共性总结出来,单独设置一个循环进行类名构建,当这个循环结束,你就可以看到css里出现了用逗号相连的col-md-1
一直到col-md-12
.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, { position: relative; width: 100%; min-height: 1px; padding-right: 15px; padding-left: 15px; }
它们都有着同样的属性,这也是@extend
的好处。但是请注意,我们并没有设置这些列的宽度,所以它们现在还是不可用的状态。
.col#{$infix}, .col#{$infix}-auto { @extend %grid-column; }
接下来有一个单独的小家伙,通过它,你可以发现.col-md
这个类也出现了,也是同样的属性,这个我们就先不去管它了。
@include media-breakpoint-up($breakpoint, $breakpoints) {}
接下拉就是大头了,我们引入了media-brakpoint-up()
函数,拿到了media查询的外包装
@media (min-width:768px){}
花括号里就是我们要往这个外包装里塞的内容了。
.col#{$infix} { flex-basis: 0; flex-grow: 1; max-width: 100%; }
首先是一个小家伙,又是这货,它单独给col-md
定义了一些flex
属性,它的意思表明,当“.col-md
”出现的时候,它将撑满整行剩余的空间。
.col#{$infix}-auto { flex: 0 0 auto; width: auto; max-width: none; }
接下来定义了“.col-md-auto
”类,这个类挺奇葩,宽度随着内容走,跟列宽毛关系都没有。
@for $i from 1 through $columns { .col#{$infix}-#{$i} { @include make-col($i, $columns); } }
接下来的@for
循环就开始定义我们的列宽了,利用make-col()
,我们得到了从col-md-1
到col-md-12
各种不同的列宽。
@for $i from 1 through $columns { .order#{$infix}-#{$i} { order: $i; } }
最后的这个@for
循环也是从1到12遍历一次,熟悉flex
的同学会知道,添加的这个order
属性就相当于名次,数值越小越靠前,而在flex
布局下的12列Grid也正是依靠这一点来对不同的列进行排序的。
至此,“md
”下的12列Grid框架构建完成。依次类推,其它的断点列也都是如此生成的。比较特殊的还是断点”xs
”,由于先前提到的原因,最小的那一部分是没有前缀的,所以你在css里看到的.col-[1-12]
就可以视作.col-xs-[1-12]
,这需要适应一下。
_grid.scss (根目录)
自动化构建其它诸如”.container
”之类的元素,那个$enable-grid-classes
布尔值,在变量里的第126行,默认为ture
。
$enable-grid-classes: true !default;
换句话说,如果你哪天心情不好,不想用bootstrap的网格系统了,直接把这里改成false
就行。
@if $enable-grid-classes { .row { @include make-row(); } .no-gutters { margin-right: 0; margin-left: 0; > .col, > [class*="col-"] { padding-right: 0; padding-left: 0; } } }
在这里面有一个新定义的类“.no-gutter
”,它这个嵌入式展开后是“.no-gutter>col
,.no-gutter>[class*=”col-”]
”,从结构可以看出来,它就是加在row
元素上的,可以取消列的默认间距。
使用建议
说回我们的Grid,我们知道,如果不加以控制,那么Bootstrap在编译Scss的时候会自动生成所有断点下的列,如果你不打算给每个等级都用上一种布局,那么自动编译的Scss将会产生大量冗余的css代码。
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, .col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, .col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, .col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, .col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, .col-xl-auto
比如我只想做桌面端和手机端两种适配,那么我可能只需要lg
和sm
的列,css中其它的列代码对我是没用的。所以面对这种情况,我们就需要对Bootstrap进行修改。这里提供两种化用方式,如果你有其它的主意,也欢迎在评论区留言。
- 在变量文件中注释掉不需要的列,与之对应的容器等级也不要忘记
$grid-breakpoints: ( xs: 0, sm: 576px, md: 768px, //lg: 992px, //xl: 1200px ) !default; $container-max-widths: ( sm: 540px, md: 720px, //lg: 960px, //xl: 1140px ) !default;
这是一种很简单的方式,也很直观,我需要什么列就用什么列,不需要的我就给扔掉,但是注意,别扔掉默认值为0的xs
。如果你不是前端洁癖患者,只是想缩减css体积,那么推荐用这种方式。
- 自己新建一个
@mixin
,替换掉默认的循环创建行为
第一种方式有一个问题,即虽然你注释掉了不需要的列,但仍需要在HTML中写入预设的类名。如果你不希望在HTML中写入一堆以”col-
”开头的类名,那么就尝试自己定义一个@mixin
,来创建自己的列吧。
创建之前注意,在bootstrap-grid.scss
中将@import “grid”
注释掉,咱们不需要自动创建。
其次,新建一个scss文件,引入bootstrap-grid
。
@import "bootstrap-grid" %grid-column{ position: relative; width: 100%; min-height: 1px; padding-left: ($grid-gutter-width/2); padding-right: ($grid-gutter-width/2); } @mixin make-my-col($breakpoint:null,$size:null,$breakpoints: $grid-breakpoints){ @extend %grid-column; @include media-breakpoint-up($breakpoint,$breakpoints){ @include make-col($size,$grid-columns); } }
在这里我提供一个自定义的@mixin
,名字也很简单make-my-col
。包含两个参数,一个是$breakpoint
(断点名),一个是$size
(列宽)。这个@mixin
其实是make-grid-columns()
的简化版。
具体原理不用多说了,因为是自用,所以我就没去考虑参数验证的问题。如果你有这方面的需求,要应用到项目中,可以考虑加上参数验证。
调用也很简单,在你需要的类中直接调用即可,传入断点名和列宽,就能创建在对应视宽下的列了。
@import "bootstrap-grid"; .side{ @include make-my-col(sm,2); @include make-my-col(md,6); } .content{ @include make-my-col(sm,10); @include make-my-col(md,6); }
P.S.写的时候注意顺序,要按照升序排列,小的放在上面,即sm在md上面,写反了md将失效。
这种方式同样有一个问题,在小型项目中,这样编译出的css能显著缩减css的体积。但在大型项目中,各种类名交错混杂,利用这种创建单个列的方式,最后生成的css代码不见得比bootstrap预定义的类名更好,所以请规范命名,一些容器元素最好保持固定宽度和固定变化。
Scss显然是利用Bootstrap更高效的方式,根据需求,以上两种方式可任选其一。当然,如果你有其它的利用方式,也可以随心所欲地蹂♂躏Bootstrap~
总结
“好的代码像一首诗”
以前对这句话只觉得莫名高大上,却没有多少感触。而在阅读了Bootstrap用Scss写的源码之后,却是真切地感受到了这一点。打开变量(_variables.scss
)文件的时候,带给我的震惊是不可言表的。严谨而有序,体量庞大而层次井然。这些模块如果一个个看下来,相信会获益良多。所以如果你和我一样,是Scss的初学者,那么浏览一下Bootstrap的源码,绝对会爽翻。
Grid应该是Bootstrap的核心区块了,从这里入手虽然比较难,但是方便从根本上了解Bootstrap的运行方式。
总的来说,Beta版本的Bootstrap4相比于Alpha版本已经往前迈了一大步,告别了传统盒模型的布局方式,转身拥抱flexbox
,同时删去了很多以前的残余代码,在初期,习惯使用b3的同学可能会觉得不大适应,具体表现是
“哎?我写了这个类咋没反应啊?”
眼下这个时候,官方说明文档都不见得会同步更新,看源代码才是最直接的阅读学习方式。
Grid篇到此结束,谢谢阅读,欢迎指出本文的错漏之处,前端新手上路,请多指教。