挑逗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版中被刪除,這裏就很少作介紹了,願它們一路走好

相關文章
相關標籤/搜索