Bootstrap栅格布局Less源码分析(ii)

前言

前面算是简单的认识了一下栅格布局是个什么东西,其实之前我写过Flex布局的学习笔记的,只不过没有上传上来,蛤蛤蛤~~

这次的还是先来介绍栅格布局的使用,等到基本上使用了再分析源码,不然都不知道再干什么。。

offset, pull, push

当我们需要改变一行中列的位置的时候,或许你会使用原生的CSS给col-*-*写样式,这样虽然也是可以的,但是Bootstrap给我们实现了三个方式改变列的位置。

offset

直接看看源码就知道这个属性可以干啥了,下面是部分的样式。

.col-xs-offset-12 {
    margin-left: 100%;
}
.col-xs-offset-11 {
    margin-left: 91.66666667%;
}
.col-xs-offset-10 {
    margin-left: 83.33333333%;
}
.col-xs-offset-9 {
    margin-left: 75%;
}
.col-xs-offset-8 {
    margin-left: 66.66666667%;
}
.col-xs-offset-7 {
    margin-left: 58.33333333%;
}

咦,为啥只有xs呢?那当然是我只选取了这一部分,其他的也都是有的,而且都是一样的,那为啥不写成这个样式呢?

.col-xs-offset-12,
.col-sm-offset-12,
.col-md-offset-12,
.col-lg-offset-12{
    margin-left: 100%;
}

因为这个是使用Less编译出来的,因为默写特殊的原因,所以就成了这种“很难看”的CSS代码。不过问题不大,蛤蛤蛤。

可以看到这个offset只是设置了一个margin-left

<div class="row">
    <div class="col-lg-4">col-lg-4</div>
    <div class="col-lg-2 col-lg-offset-4">col-lg-2</div>
</div>

显示的效果如下。

1575882611459

push pull

之前我们看了.col-*-*的公共样式,不知道我们是否关注到一点,那就是所有的col都是position: relative,都是相对布局的。那么这就意味着我们是可以通过left top进行位置的布局的。看一下源码。

.col-sm-push-12 {
    left: 100%;
}
.col-sm-push-11 {
    left: 91.66666667%;
}
.col-sm-push-10 {
    left: 83.33333333%;
}
...
.col-md-pull-12 {
    right: 100%;
}
.col-md-pull-11 {
    right: 91.66666667%;
}
.col-md-pull-10 {
    right: 83.33333333%;
}

其中col-lg-push-2就是向右移动2格,col-lg-pull-2是向左移动两格。不过为啥push是向右,而pull是向左呢?可以这样理解吧,元素的默认布局方式就是从左向右,如果我们推(push)他的话,一般就是促进他的运动。他就是向右。同理,拉他就是向左。。。感觉这个解释像一个憨比~。

需要注意的是使用pull或者push改变元素的位置的时候,可能会和其他的列重复,但是并不会将其他的元素覆盖,毕竟只是使用相对布局不会脱离文档流,如果想要对其他的元素进行覆盖,可以选择使用z-index: 1,这样就可以覆盖那么没有设置z-index的元素了。

使用pushpull可以对不同的列进行调换顺序。比如如下的代码。

<div class="row">
    <div class="col-lg-4 col-lg-push-8">col-lg-4</div>
    <div class="col-lg-2 col-lg-push-2">col-lg-2</div>
    <div class="col-lg-6 col-lg-pull-6">col-lg-6</div>
</div>

按道理说不设置push pull显示的顺序应该是4 2 6,设置了push pull之后显示的效果如下图所示。

1575884028340

刚好反过来了。不过这只使用lg模式,如果想要其他模式也可以,可以设置相应的push pull

源码

想要看源码就要先下载Less源码。在less目录下就是项目的所有的Less源码。第一级目录中就有一个grid.less文件。所谓的grid就是栅格的意思。栅格布局的实现就在这个文件中。

还有三个相关的文件,mixin中的grid.lessgrid-frameword.less用来定义grid中使用到的混合。variable.less定义了源码中的所有的变量。

看根目录下的grid.less文件的一开始。

container 和 container-fluid的初始化

// 容器的宽度
//
// 设置容器的宽度,然后在媒体查询中重写固定的导航栏

.container {
  .container-fixed(); // 调用了mixins/grid.less中的混合

  @media (min-width: @screen-sm-min) { // @screen-sm-min => @screen-sm => 768px
    width: @container-sm;
  }
  @media (min-width: @screen-md-min) { // @screen-md-min => @screen-md => 992px
    width: @container-md;
  }
  @media (min-width: @screen-lg-min) { // @screen-lg-min => @screen-lg => 1200px
    width: @container-lg;
  }
}

我们先来看看.container-fixed()这个混合干了什么事情。

// 生成容器元素
.container-fixed(@gutter: @grid-gutter-width) {
  margin-right: auto;
  margin-left: auto;
  padding-left:  floor((@gutter / 2));
  padding-right: ceil((@gutter / 2));
  &:extend(.clearfix all); // 清除浮动
}

//-------------------------------------
// variable.less
// Padding between columns. Gets divided in half for the left and right.
@grid-gutter-width:         30px;

//-------------------------------------
// clearfix.less
.clearfix() {
  &:before,
  &:after {
    content: " "; // 1
    display: table; // 2
  }
  &:after {
    clear: both;
  }
}

上面调用混合的时候没有使用参数,那么这里的调用就是使用默认的参数。所谓的grid-gutter-width就是设置列与列之间的空隙,然后平均分给两边设置padding。这个混合中还继承了.clearfix,这样所有的容器就都是清除了浮动了的。

下面再来看看后面的媒体查询。

// variable.less

//** Deprecated `@screen-lg` as of v3.0.1
@screen-lg:                  1200px;
@screen-lg-min:              @screen-lg;

//** Deprecated `@screen-md` as of v3.0.1
@screen-md:                  992px;
@screen-md-min:              @screen-md;

//** Deprecated `@screen-sm` as of v3.0.1
@screen-sm:                  768px;
@screen-sm-min:              @screen-sm;

@container-large-desktop:      (1140px + @grid-gutter-width);
//** For `@screen-lg-min` and up.
@container-lg:                 @container-large-desktop;

@container-desktop:            (940px + @grid-gutter-width);
//** For `@screen-md-min` and up.
@container-md:                 @container-desktop;

@container-tablet:             (720px + @grid-gutter-width);
//** For `@screen-sm-min` and up.
@container-sm:                 @container-tablet;

看来显示的大小还和我们的gutter有关。

上面就是对.container进行初始化,生成之前我们看过的那个代码。

.container {
  padding-right: 15px;
  padding-left: 15px;
  margin-right: auto;
  margin-left: auto;
}
@media (min-width: 768px) {
  .container {
    width: 750px;
  }
}
@media (min-width: 992px) {
  .container {
    width: 970px;
  }
}
@media (min-width: 1200px) {
  .container {
    width: 1170px;
  }
}

然后就是container-fluid的初始化。

.container-fluid {
  .container-fixed();
}

除了没有上面的媒体查询,其他都是一样的。

行和列的初始化

// Row
//
// Rows contain and clear the floats of your columns.

.row {
  .make-row();
}

//---------------------------
// mixins/grid.less
// Creates a wrapper for a series of columns
.make-row(@gutter: @grid-gutter-width) {
  margin-left:  ceil((@gutter / -2));
  margin-right: floor((@gutter / -2));
  &:extend(.clearfix all);
}

上面是对行的创建过程,非常的简单易懂, 每个行也都是清除了浮动的。编译出来的代码如下。

.row {
  margin-right: -15px;
  margin-left: -15px;
}
// Columns
//
// Common styles for small and large grid columns

.make-grid-columns();

//--------------------------------
// mixins/grid-frameword.less
.make-grid-columns() {
  // Common styles for all sizes of grid columns, widths 1-12
  .col(@index) { // initial
    @item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
    .col((@index + 1), @item);
  }
  .col(@index, @list) when (@index =< @grid-columns) { // general; "=<" isn't a typo
    @item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
    .col((@index + 1), ~"@{list}, @{item}");
  }
  .col(@index, @list) when (@index > @grid-columns) { // terminal
    @{list} {
      position: relative;
      // Prevent columns from collapsing when empty
      min-height: 1px;
      // Inner gutter via padding
      padding-left:  ceil((@grid-gutter-width / 2));
      padding-right: floor((@grid-gutter-width / 2));
    }
  }
  .col(1); // kickstart it
}

//-----------------
// variable.less
// Number of columns in the grid.
@grid-columns:              12;

这次的这个代码就非常的恐怖了,需要好好的看了。

.make-grid-columns中定义了三个混合,然后再最后调用.col(1),这属性的样式,在C++也非常的常见,看来是要进行递归了。

首先.col(1)只有一个参数那么只可以调用第一个混合。定义了一个变量@item,使用我们之前说的那个避免编译,而且当变量不是在属性值中使用的时候,在url 选择器 属性名 import等地方使用的时候,都是需要加上{}的。此时item值如下。

@item = '.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1'

然后调用.col(2, @item),这时候调用第二个混合,因为只满足第二个混合的条件@index =< @grid-colums

在这个混合中也定义了一个@item。注意之前也说过,less中的变量是有作用域的,两个@item是不干扰的。此时item值如下。

@item = '.col-xs-2, .col-sm-2, .col-md-2, .col-lg-2'

然后调用.col(2, @{list}, @{item}),显然这已经是一个递归了,不过第二个参数究竟是什么呢?很容易看出来此时的第二个参数就是下面的这个。

.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2

index = 13的时候退出递归,经历了这么多次的递归之后第二个参数变成了。

.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2
.......
.col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12

此时调用第三个混合。将上面的@list作为选择器进行设置属性。果然是高端操作。

编译出来就是之前我们的看的那个代码。

.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
  position: relative;
  min-height: 1px;
  padding-right: 15px;
  padding-left: 15px;
}

生成栅格

上面都还只是给行列设置一定的margin padding,清除浮动这些小操作而已,还没到栅格布局的地方。

下面就是grid.less文件中的最后的代码,媒体查询的样式设置。

// Extra small grid
//
// Columns, offsets, pushes, and pulls for extra small devices like
// smartphones.

.make-grid(xs);


// Small grid
//
// Columns, offsets, pushes, and pulls for the small device range, from phones
// to tablets.

@media (min-width: @screen-sm-min) {
  .make-grid(sm);
}


// Medium grid
//
// Columns, offsets, pushes, and pulls for the desktop device range.

@media (min-width: @screen-md-min) {
  .make-grid(md);
}


// Large grid
//
// Columns, offsets, pushes, and pulls for the large desktop device range.

@media (min-width: @screen-lg-min) {
  .make-grid(lg);
}


//----------------------------------
// grid-framework.less
// Create grid for specific class
.make-grid(@class) {
  .float-grid-columns(@class);
  .loop-grid-columns(@grid-columns, @class, width);
  .loop-grid-columns(@grid-columns, @class, pull);
  .loop-grid-columns(@grid-columns, @class, push);
  .loop-grid-columns(@grid-columns, @class, offset);
}

float-grid-columns开始看起吧。

.float-grid-columns(@class) {
  .col(@index) { // initial
    @item: ~".col-@{class}-@{index}";
    .col((@index + 1), @item);
  }
  .col(@index, @list) when (@index =< @grid-columns) { // general
    @item: ~".col-@{class}-@{index}";
    .col((@index + 1), ~"@{list}, @{item}");
  }
  .col(@index, @list) when (@index > @grid-columns) { // terminal
    @{list} {
      float: left;
    }
  }
  .col(1); // kickstart it
}

传入的@class可能值是xs sm md lg。看到上面的代码,我感觉一秒钟我们就可以看出编译出来的结果是什么了。还是那个递归的套路,将所有的col-@{class}-*设置了左浮动!编译结果就不写了。

该混合是将所有的列都设置左浮动。

然后就是.loop-grid-columns,这是一个匹配模式的混合,通过第三个参数对不同的混合进行匹配。

// Basic looping in LESS
.loop-grid-columns(@index, @class, @type) when (@index >= 0) {
  .calc-grid-column(@index, @class, @type);
  // next iteration
  .loop-grid-columns((@index - 1), @class, @type);
}

.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {
  .col-@{class}-@{index} {
    width: percentage((@index / @grid-columns));
  }
}

这是个.loop-grid-columns统一进了一个混合,看来还不是立马进行匹配的。现在以..loop-grid-columns(@grid-columns, @class, width)为例进行说明。

@type = width进入了.calc-grid-column,还是一样的套路,将col-md-1这种样式的设置宽度,如果是1,那么width就是1/12的父元素,但是要转成百分比,于是调用了percentage函数(这是一个less函数,将数字转换成为百分比)。

编译之后就是类似这样的。

.col-xs-12 {
  width: 100%;
}
.col-xs-11 {
  width: 91.66666667%;
}
.col-xs-10 {
  width: 83.33333333%;
}
.col-xs-9 {
  width: 75%;
}

这个我们也看过了。

然后就是.loop-grid-columns(@grid-columns, @class, pull);

.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {
  .col-@{class}-push-@{index} {
    left: percentage((@index / @grid-columns));
  }
}
.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {
  .col-@{class}-push-0 {
    left: auto;
  }
}

这次有两个函数,因为这里需要对.col-md-0设置样式,left: auto

push offset都是基本上相同的用法。

.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {
  .col-@{class}-pull-@{index} {
    right: percentage((@index / @grid-columns));
  }
}
.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {
  .col-@{class}-pull-0 {
    right: auto;
  }
}
.calc-grid-column(@index, @class, @type) when (@type = offset) {
  .col-@{class}-offset-@{index} {
    margin-left: percentage((@index / @grid-columns));
  }
}

上面基本上就是栅格布局的所有的less源码,可以看到源码其实非常短,但是编译出来的css代码是非常多的,可见less是真的less,写的少做的多。

总结

我们之所以看这个栅格布局的源码,一方面是我们需要学习Bootstrap的栅格布局如何使用,然后就是学会使用媒体查询还有就是less中递归的使用。


一枚小菜鸡