精讀《用 css grid 從新思考佈局》

1 引言

Flex 與 Grid 相比就像功能鍵盤和觸摸屏。觸摸屏的控制力相比功能鍵盤來講就像是降維打擊,由於功能鍵盤只能上下左右控制(x、y 軸),而觸摸屏打破了佈局障礙,直接從(z 軸)觸達,這樣 不管 UI 內部佈局再複雜,均可以經過 touch 直接定位。css

Flex 是一維佈局方式,咱們須要不斷嵌套 Div 才能造成複雜結構,而一旦佈局產生了變化,原有嵌套結構若是不能 「兼容變化」 到新結構,代碼就須要重構。而 Grid 就像觸摸屏同樣,能夠二維佈局,即使佈局方式作了翻天覆地的調整,也僅需少許修改就能適配。html

這就是此次精讀 用 css grid 從新思考佈局 的緣由,理解這個革命性佈局技術給佈局,甚至代碼邏輯組織帶來的變化。前端

2 概述

做者首先拋出了 Flex 的問題,實際上是 block float flex 這三種佈局模式的通病:css3

  • 佈局結構由 Div 層級結構描述,致使 Div 層級複雜且遇到結構變動時難以維護。
  • 定製能力弱。Flex 佈局有一些不受控制的智能設定,好比寬度 50% 的子元素會被同級元素擠到 50% 如下,這種智能化在某些場景是須要的,但因爲沒有提供像 Grid 的 minmax 之類的 API,因此定製型不足。

舉個例子,上圖的結構用 Flex 描述多是這樣的:git

<div class="card">
  <div class="profile-sidebar">
    <img src="https://i.pravatar.cc/125?image=3" alt="" class="profile-img" />
    <ul class="social-list">
      <li>
        <a href="#" class="social-link"
          ><i class="fab fa-dribbble-square"></i
        ></a>
      </li>
      <li>
        <a href="#" class="social-link"
          ><i class="fab fa-facebook-square"></i
        ></a>
      </li>
      <li>
        <a href="#" class="social-link"
          ><i class="fab fa-twitter-square"></i
        ></a>
      </li>
    </ul>
  </div>
  <div class="profile-body">
    <h2 class="profile-name">Ramsey Harper</h2>
    <p class="profile-position">Graphic Designer</p>
    <p class="profile-info">
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere a tempore,
      dignissimos odit accusantium repellat quidem, sit molestias dolorum
      placeat quas debitis ipsum esse rerum?
    </p>
  </div>
</div>

利用 HTML 嵌套結構,咱們將圖形縱向分紅兩大塊,而後在每塊內部繼續嵌套劃分佈局,這是最經典的佈局行爲了。github

樣式文件裏,咱們須要對每層佈局進行描述,同時支持多分辨率彈性佈局,包括頂層 card 容器在內的一些樣式須要作必定調整:微信

.card {
  width: 80%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  max-width: 600px;
  background: #005e9b;
  flex-basis: 250px;
  color: white;
  padding: 2em;
  text-align: center;
}

.profile-info {
  font-weight: 300;
  opacity: 0.7;
}

.profile-sidebar {
  margin-right: 2em;
  text-align: center;
}

.profile-name {
  letter-spacing: 1px;
  font-size: 2rem;
  margin: 0.75em 0 0;
  line-height: 1;
}

.profile-name::after {
  content: "";
  display: block;
  width: 2em;
  height: 1px;
  background: #5bcbf0;
  margin: 0.5em auto 0.65em;
  opacity: 0.25;
}

.profile-position {
  text-transform: uppercase;
  font-size: 0.875rem;
  letter-spacing: 3px;
  margin: 0 0 2em;
  line-height: 1;
  color: #5bcbf0;
}

.profile-img {
  max-width: 100%;
  border-radius: 50%;
  border: 2px solid white;
}

.social-list {
  list-style: none;
  justify-content: space-evenly;
  display: flex;
  min-width: 125px;
  max-width: 175px;
  margin: 0 auto;
  padding: 0;
}

.social-link {
  color: #5bcbf0;
  opacity: 0.5;
}

.social-link:hover,
.social-link:focus {
  opacity: 1;
}

.bio {
  padding: 2em;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

@media (min-width: 450px) {
  .bio {
    text-align: left;
    max-width: 350px;
  }
}

.bio-title {
  color: #0090d1;
  font-size: 1.25rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  line-height: 1;
  margin: 0;
}

.bio-body {
  color: #555;
}

.profile {
  display: flex;
  align-items: flex-start;
}

@media (min-width: 450px) {
  .card {
    flex-direction: row;
    text-align: left;
  }

  .profile-name::after {
    margin-left: 0;
  }
}

讓咱們看看 Grid 是怎麼作的吧!Grid 有許多 API,咱們重點看 grid-template-areas 這個屬性,利用它,咱們能夠不關心模塊的 HTML 結構,直接平鋪方式描述:ide

<div class="card">
  <img src="https://i.pravatar.cc/125?image=3" alt="" class="profile-img" />
  <ul class="social-list">
    <li>
      <a href="#" class="social-link"><i class="fab fa-dribbble-square"></i></a>
    </li>
    <li>
      <a href="#" class="social-link"><i class="fab fa-facebook-square"></i></a>
    </li>
    <li>
      <a href="#" class="social-link"><i class="fab fa-twitter-square"></i></a>
    </li>
  </ul>
  <h2 class="profile-name">Ramsey Harper</h2>
  <p class="profile-position">Graphic Designer</p>
  <p class="profile-info">
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere a tempore,
    dignissimos odit accusantium repellat quidem, sit molestias dolorum placeat
    quas debitis ipsum esse rerum?
  </p>
</div>

能夠看到,使用 Grid 能夠將 UI 結構與 HTML 結構分離,HTML 結構僅描述包含關係,咱們只需在樣式文件中描述具體 UI 結構。模塊化

樣式文件只截取 Grid 相關部分:佈局

.card {
  width: 80%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  max-width: 600px;
  background: #005e9b;
  flex-basis: 250px;
  color: white;
  padding: 2em;
  text-align: left;

  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-column-gap: 2em;
  grid-template-areas:
    "image name"
    "image position"
    "social description";
}

.profile-name {
  grid-area: name;
}
.profile-position {
  grid-area: position;
}
.profile-info {
  grid-area: description;
}
.profile-img {
  grid-area: image;
}
.social-list {
  grid-area: social;
}

能夠看到,grid-template-areas 是進一步抽象的語法,將頁面結構經過直觀的文本描述,不管是理解仍是修改都更爲輕鬆。

這種描述方式適配不一樣分辨率下也具備優點,只要重組 grid-template-areas 便可:

@media (min-width: 600px) {
  .card {
    text-align: left;
    grid-template-columns: 1fr 3fr;
    grid-template-areas:
      "image name"
      "image position"
      "social description";
  }
}

歸根結底,Grid 經過二維結構描述,將子元素佈局控制收到了父級,使佈局描述更加直觀。

最後做者也提到,Flex 依然有使用場景,即簡單的一維結構,或者 space-between 等 Flex 獨有語法的狀況。所以推薦總體、複雜的二維佈局採用 Grid,一維的簡單佈局採用 Flex。

3 精讀

Grid 的佈局思路給了我不少啓發,HTML 結構與 UI 結構的分離有助於減小 DIV 的層級結構,使代碼看上去更清晰。

也許有人會疑惑,Grid 無非將 HTML 佈局部分功能挪到了 CSS,總體複雜度應該不變。其實,從 grid-template-areas 這個 API 能夠看到,Grid 不只僅將佈局功能抽到 CSS 中,更是將佈局描述進行了一層抽象,使代碼更易維護。

抽象,再抽象

爲何 Grid 能夠對佈局進行抽象?由於 Grid 將二維結構都掌握在手中,獲得了更大的佈局能力,才能進一步將結構化語法抽象爲字符串的描述。

抽象的好處是不言而喻的,你以爲一堆嵌套的 DIV 與下面的代碼,哪一個更易讀呢?

.card {
  grid-template-areas:
    "image name"
    "image position"
    "social description";
}

這就是抽象的好處,通常來講,代碼抽象程度越高就越易讀,越易維護。

再看一個 Chrome Grid 插件,將 Grid 可視化顯示出來,並能夠以 UI 方式進行調整:

UI 是對文本的再抽象,同時能夠規避一些不可能存在的語法,好比:

.card {
  grid-template-areas:
    "image name"
    "image position"
    "social image";
}

佈局只能以凸多邊形方式拓展,不可能分離,也不可能忽然插入一個其餘模塊而變成凹多邊形。所以 UI 能夠將這個錯誤規避,並簡化爲橫豎多條線的方式對 UI 進行劃分,顯然這種描述方式效率更高。

不得不說,Grid 以及圖形化插件的探索,是佈局領域的一大進步,是不斷抽象的嘗試,要解決的問題只有一個:如何提供一種更直觀的描述 UI 的方式。

佈局對模塊化的影響

Grid 將佈局方式提升了一個維度,會直接影響到 JS 模塊化方式。

尤爲是以 JSX 組織代碼的狀況下,一個模塊等於 UI + JS,經過嵌套方式的佈局會讓咱們更傾向於站在 UI 視角劃分模塊。

好比對於上圖模塊,若是用 Flex 方式佈局,咱們可能會首先建立模塊 X 做爲左側容器,子元素是 A 和 B,建立模塊 Y 做爲右側容器,子元素是 C 以及新容器 Z,Z 容器的子元素是 D 和 E。

若是你的第一印象是這麼組織代碼,不得不認可模塊化會受到佈局方式的影響。雖然許多時候這樣劃分是正確的,但當這 5 個模塊各自沒有關聯時,咱們建立的容器 X、Y、Z 就失去了複用性,在新的組合場景咱們又要從新組合一遍。

可是在 Grid 語法中,咱們不須要 X、Y、Z,只須要用 css grid generator 按照上圖的方式拖拖拽拽便可自動生成以下佈局代碼:

.parent {
  display: grid;
  grid-template-columns: 3fr repeat(2, 1fr);
  grid-template-rows: repeat(5, 1fr);
  grid-column-gap: 0px;
  grid-row-gap: 0px;
}

.div1 {
  grid-area: 1 / 1 / 3 / 2;
}
.div2 {
  grid-area: 3 / 1 / 6 / 2;
}
.div3 {
  grid-area: 1 / 2 / 2 / 4;
}
.div4 {
  grid-area: 2 / 2 / 6 / 3;
}
.div5 {
  grid-area: 2 / 3 / 6 / 4;
}

其實 grid-template-columns grid-template-rows 組合起來使用比 grid-template-areas 更強大,可是純代碼方式描述沒有 grid-template-areas 直觀,但是配合一些可視化系統就很是直觀了:

將 A ~ E 這 5 個模塊佈局抽出來後,它們之間的關係就打平了,咱們能夠徹底從邏輯視角審視如何作模塊化了。

4 總結

CSS Grid 本質上是一種二維佈局的語法,相比 BlockFlex 等一維佈局方案,多了一個維度能夠同時從行與列角度定義佈局,所以派生出 grid-template-areas 等語法,總體上更內聚更直觀,抽象度也更高了。

理解了這些也就理解了佈局將來的發展方向,讓佈局與 Dom 分離 一直是前端的一個夢想,開發 UI 部分時,只需關心頁面由哪些模塊組成,去實現這些模塊就好了,而不須要關心模塊之間應該如何組合。在描述組合時,能夠經過可視化或比較抽象的字符串描述佈局的結構,並對應到寫好的模塊上,這樣的代碼維護性遠高於用 DIV 描述結構的方案。

討論地址是: 精讀《用 css grid 從新思考佈局》 · Issue #211 · dt-fe/weekly

若是你想參與討論,請 點擊這裏,每週都有新的主題,週末或週一發佈。前端精讀 - 幫你篩選靠譜的內容。

關注 前端精讀微信公衆號

<img width=200 src="https://img.alicdn.com/tfs/TB...;>

版權聲明:自由轉載-非商用-非衍生-保持署名( 創意共享 3.0 許可證
相關文章
相關標籤/搜索