挑逗Bootstrap4源代码 - Grid篇(上)

本文所引用的版本为Bootstrap 4 Beta版,阅读者请先下载好相关源文件。css

时光荏苒,若后续版本代码发生变化,将看心情进行更新补充。若是你以为本文不错,欢迎评论支持,如需转载请标明做者及出处,谢谢。前端

在平常使用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的引用

要素分析

变量(_variales.scss):

  1. $grid-breakpoints:
$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
) !default;
@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
@include _assert-starts-at-zero($grid-breakpoints);

分为xssmmdlgxl五个等级,分别表示极小中等超大。这个断点设置主要用于媒体查询,即当浏览器视窗横向尺寸发生变化时,一旦到了指定条件,好比width=768px,就将触发设置在md断点下的样式。这些等级的数据能够按需改动,全局凡是引用这个map的都将受到影响。 ide

在文档注释中提到,这里设置的数值表示变化的最小值,即触发md的条件是≥768px。函数

在变量$grid-breakpoints后跟着两个@mixin,这两个@mixin定义在根目录下_function.scss文件中,都起一个判断做用,其中_assert-ascending()是确保整个$grid-breakpoints的内容是按升序排列,即从小到大;_asseert-starts-at-zero()是确保$grid-breakpoints的第一个元素值必须为0,即xs必须为0。布局

  1. $container-max-widths:
$container-max-widths: (
  sm: 540px,
  md: 720px,
  lg: 960px,
  xl: 1140px
) !default;
@include _assert-ascending($container-max-widths, "$container-max-widths");

相比较于网格断点变量,这里的容器变量删去了xs等级,只余下了4个等级。这并不违背逻辑,由于在注释中,这里写的数值表示容器宽度的最大值。举个例子,在md条件下,视窗最小宽度为768px,而容器最大宽度为720px,留有必定的余地。因此这样的话,数值为0xs在容器宽度这里是没有意义的,标记为sm的容器宽度值就是在0-540px之间变更,正对应着视窗宽度xssm的区间。变量后紧跟着的函数以前已经提过,这里再也不赘述。测试

  1. grid-columns:
$grid-columns: 12 !default;
$grid-gutter-width: 30px !default;

这里的grid-columns指的是包括$grid-columns$grid-gutter-width在内的网格列定义。
这二者($grid-columns$grid-gutter-width)都是初始定义,分别表示总列数列间隔,在以前的alpha6版本中,还有一个不一样视宽下的gutter组,在beta版本中已被删除。flex

至此,网格涉及的变量已经介绍完,后续的全部mixin和函数都是基于这些数值的,因此这里的数值很重要,牵一发而动全身。ui

@mixin(mixins文件夹)

  1. _breakpoints.scss:
  • breakpoint-next:
@function breakpoint-next(
  $name, 
  $breakpoints: $grid-breakpoints, 
  $breakpoint-names: map-keys($breakpoints)) 
{
  $n: index($breakpoint-names, $name);
  @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
}

该函数看似有三个,实际只有两个参数,一个是$name,即断点名,需手动输入,第二个是在变量中定义的断点的名称列表。该函数的做用就是返回输入的断点名的下一个断点,若是没有下一个了,那就返回空值。即breakpoint-next(“sm”)将返回mdbreakpoint-next(“xl”)将返回null。

  • breakpoint-min、breakpoint-max:
@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) - 1px, null);
}

这两个函数光看函数名很容易引发误会,它们的做用绝非获取断点列表中的最大值和最小值,由于咱们在事先定义断点列表时就已经确认过断点列表是按照升序排列的了。这里的min,max指的是当前选中等级的区间左右值。因此这两个函数都包含$name参数,当对“md”分别应用这两个函数时,获得的值将是768px(min)和991px(max)。

这里有一个问题,当使用breakpoint-max()函数获取区间右值时,为了避免与下一个断点数值上重合,因此进行了一个减一操做(官方的膜法)。

另外就是经过这个函数,可以知道新的Bootstrap4的一个小坑,在Bootstrap3中,是有等级xs的,表明极小,而在新版本中,因为xs设定值的特殊性(断点值为0),因此从宽度定义上,xs这个等级就被取消了,这里的min函数,在$name=“xs”的状况下将返回空值,这样就从数值上去掉了xs。在后面提到的breakpoint-infix函数中,将从类名定义层次上取消”xs”等级。

  • breakpoint-infix:
@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
  @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
}

核心函数,直接关系到类名的自动化生成。它的功能是利用前面breakpoint-min()函数,将断点名以“-name”的形式返回,即breakpoint-infix(“md”)将返回“-md”,换句话说,这个函数的做用就是给断点名前头加个短横线,等到时候须要循环建立列的时候,就能够利用这个函数动态生成诸如”.col-md-4”之类的类名了。

在这个函数中有一个判断,即若是breakpoint-min()函数返回的值是null,那么整个函数将返回一个空字符串,而在min函数中,只有等级为“xs”时,才会返回null,因此,在建立列的类名时,你将再也看不见“.col-xs-4”,取而代之的是”.col-4”。在从b3迁移至b4的时候,这一点要尤为注意。

  • media-breakpoint-up
  • media-breakpoint-down:
@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
  $min: breakpoint-min($name, $breakpoints);
  @if $min {
    @media (min-width: $min) {
      @content;
    }
  } @else {
    @content;
  }
}
@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
  $max: breakpoint-max($name, $breakpoints);
  @if $max {
    @media (max-width: $max) {
      @content;
    }
  } @else {
    @content;
  }
}

关于media的都用@mixin定义,而不是前面的@function了。这里的两个mixin,功能也简单,就是利用前面提到的breakpoint-minbreakpoint-max函数,定义变化节点的media query,这样在建立网格时,就能根据预先设定的几个等级来进行响应式变化了。这里的updown,能够理解成“大于(up)”、”小于(down)”,亲测,在后续的应用中,基本都是用的media-breakpoint-up。若是你打算重写Bootstrap,那么用用down好像也不错。

  • media-breakpoint-between
  • media-breakpoint-only:
@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
  $min: breakpoint-min($lower, $breakpoints);
  $max: breakpoint-max($upper, $breakpoints);

  @media (min-width: $min) and (max-width: $max) {
    @content;
  }
}
@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
  $min: breakpoint-min($name, $breakpoints);
  $max: breakpoint-max($name, $breakpoints);

  @if $min != null and $max != null {
    @media (min-width: $min) and (max-width: $max) {
      @content;
    }
  } @else if $max == null {
    @include media-breakpoint-up($name)
  } @else if $min == null {
    @include media-breakpoint-down($name)
  }
}

候补@mixin,分别表明两种宽度之间和仅在一种宽度下的情形。between好说,利用前面的updown两个@mixin表示在某个区间范围内的情形,能够用来跨等级,好比说给“sm”一种排版,而后给“md”到“xl”一种排版(真的有人会这么干嘛?)。而only这个@mixin有些奇怪,在Alpha6中它将between包在了其中,在Beta中它也作了大体相同的事,只是多进行了一些判断。并且正如其函数名所示,它表示“仅”,直接把表达式写出来可能更直观,(前缀省略)

only(“md”)=between(“md”,”md”)

就是这样一种奇怪的函数,不排除在后续对Bootstrap进行拆解时会再见到它,不过目前,它对咱们没什么用处。

综上,除了一些辅助函数,咱们在后续的网格搭建中会用到的函数或者@mixin只有俩,一个是breakpoint-infix($name),一个是media-breakpoint-up($name)

  1. _grid.scss

这里指的_grid.scss是指的mixins文件夹下的_grid.scss,而非根目录下的_grid.scss

关于这个@mixin集合,一言以蔽之,即,想建网格就靠它。
这是一个网格搭建的基础集合,但单靠它,咱们仍是建立不出Bootstrap引觉得傲的12列网格系统的,想提早知道缘由的话能够打开_grid-frameworks.scss文件先看看。

顺带一提,和Alpha6版本不一样的是,_grid.scss删除了@mixin make-gutter(),大概是官方以为写这么一个@mixin有点画蛇添足吧。

  • make-container:
@mixin make-container() {
  margin-right: auto;
  margin-left: auto;
  padding-right: ($grid-gutter-width / 2);
  padding-left:  ($grid-gutter-width / 2);
  width: 100%;
}

建立一个相对定位的容器,也就是你们熟悉的.container的本体……的一部分。嗯,是的,一部分。若是你新建了一个

<div class=”main”></div>

在scss中写一个

.main{@include make-container();}

你建立的其实是一个在Bootstrap中以“container-fluid”为类名的流体容器,仔细观察这个@mixin,你就会发现,它指定了容器宽度为100%,在实际的浏览器中的表现为横向全屏,任凭你调整浏览器的窗口大小,这一点都不会变(固然,它自带一个左右padding)。

  • make-container-max-width:
@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-container,就能建立出你们熟悉的.container了(其实这一点在根目录下的_grid.scss就能够找到)。在这个@mixin里,它肯定了width=$container-max-width(在变量中有定义),这就表明着根据这个@mixin建立出的容器,不会再像流体容器那样宽度为所欲为,而是呈阶梯性变化,某种程度上,这更符合咱们的预期和使用习惯。

  • make-row:
@mixin make-row() {
  display: flex;
  flex-wrap: wrap;
  margin-right: ($grid-gutter-width / -2);
  margin-left:  ($grid-gutter-width / -2);
}

这里是一个很重要的变化,你们能够注意到,row这里的display变成了flex,这也是b4主要的改进之一,row这个基础构建的变化意味着整个b4框架在很大程度上都会创建在flexbox的基础上(IE8滚蛋吧)。

顺带吐个槽,你们能够注意到,make-row里的循环给每一个row加上了一个负margin,大小也正是gutter/2,(即15px,若是你没改的话),目测是为了抵消建立容器(container)时padding的影响,因此说……嗯……当初为啥加个padding呢?

  • make-column-ready:
@mixin make-col-ready() {
  position: relative;
  width: 100%;
  min-height: 1px; 
  padding-right: ($grid-gutter-width / 2);
  padding-left:  ($grid-gutter-width / 2);
}

我很奇怪Bootstrap建立了这个@mixin却没有使用它,在后面的_grid-frameworks.scss中找到了缘由,这个@mixin被替换成了一个占位符。这应该是目前内测版的一个小疏漏,后续版本要么删除这个@mixin,要么把占位符那一块进行更新。总之,这个@mixin咱们先略过不谈。

  • make-col:
@mixin make-col($size, $columns: $grid-columns) {
  flex: 0 0 percentage($size / $columns);
  max-width: percentage($size / $columns);
}

嗯,列终于也变成flex布局了,之后等高列,元素垂直居中就很简单了。这里简单解释下这个@mixin,参数$size为整数,从1到12,它的列宽计算也正是基于此,经过$size/$columns获得占比,以这个百分数结果为列宽,这使得其具有必定的响应性。

顺带说明一下这个flexflex:0 0 <number>这是个简写属性,它的具体意思为不放大也不缩小(对应状况为有剩余空间或剩余空间不足)的宽度(或主轴空间,通常为横向)为<number>的flex元素。

这里也有一个小坑,我在刚开始测试时,觉得make-col()就是12列布局的奥秘所在,因而建立了两个div,分别给它们加上make-col(4)make-col(8)

&lt;div class="container"&gt;
  &lt;div class="side"&gt;...&lt;/div&gt;
  &lt;div class="main"&gt;...&lt;/div&gt;
&lt;/div&gt;
.side{
  @include make-col(4);
}
.main{
  @include make-col(8);
}

刚开始,它们也的确如我所想的呈1:2宽度分布,可是当我缩小窗口到必定程度的时候,惊恐地发现它们并无折行,而是发生了重叠。

究其缘由,就是出在这个make-col()上,首先,咱们惯常是习惯在列上包一层rowrow中咱们定义了flex-wrapwrap,理论上row中的元素在宽度不够时会折行,可是靠make-rol()定义的“.col-<number>”的类(为何没有前缀以前在infix中提过了)的宽度倒是百分比数,换句话说,它的宽度永远是够的(row内的col宽度和永远小于等于100%),这样,当你的内容缩小到必定程度的时候,势必会出现溢出现象(容器宽度小于内容宽度)。当你手动调整<number>,使其和大于12时,好比make-col(4)make-col(9),但这就至关于手动折行了,效果不算特别理想。

因此通过分析,实际使用中依然须要搭配前缀,好比大窗口下用md,而这个不带前缀的类则能够视做xs的替代品,在极小窗口下使用它,但不要单独使用,否则在小窗口状况下,将会出现溢出状况,若是在页面元素多的状况下,影响会很大。

活在天堂的offset、push、pull,Alph6中存在而在Beta版中被删除,这里就很少作介绍了,愿它们一路走好

相关文章
相关标签/搜索