原文:CSS Grid VS Flexbox: A Practical Comparison
作者:Danny Markov
在不久前,所有 HTML 頁面的布局都是通過table、float和其他 CSS 屬性來完成的,但其實這些屬性并不適合設計復雜的網(wǎng)頁。
緊接著,出現(xiàn)了flexbox ——一種專門為創(chuàng)建強大的響應式頁面而設計的布局模式。 使得元素和內(nèi)容的正確對齊變得很容易,現(xiàn)在是大多數(shù)網(wǎng)頁開發(fā)者首選的 CSS 系統(tǒng)。
現(xiàn)在我們有了一個“構(gòu)建 html 布局最佳系統(tǒng)獎杯”的新競爭者——這就是強大的CSS Grid,到本月底,它將在 Firefox 52和 Chrome 57中發(fā)布,其他瀏覽器(希望如此)也會很快跟進。
基本布局測試
為了了解使用每個系統(tǒng)構(gòu)建布局是什么感覺,我們對同一個 HTML 頁面構(gòu)建了兩次 -- 一次是使用 flexbox,另一次是使用CSS Grid。 您可以從原文文章頂部附近的下載按鈕下載這兩個項目,或者在這個在線演示中查看它們:
這個設計非常簡單——它由一個居中的容器組成,其中包含一個頭部、主要部分、側(cè)邊欄和一個頁腳。 下面是我們必須解決的主要"挑戰(zhàn)",同時盡可能保持 CSS 和 HTML 的干凈:
- 布局四個主要部分
- 響應式布局(在屏幕較小的情況下側(cè)邊欄位于主要內(nèi)容的下方)
- 將標題導航的內(nèi)容對齊到左側(cè),按鈕對齊到右側(cè)
正如你所看到的,為了便于比較,我們把一切都寫的盡可能簡單。 讓我們從第一個問題開始。
挑戰(zhàn)1: 定位頁面部分
Flexbox 方案
我們從 flexbox 方案開始。 我們將 display: flex添加到容器并將其子容器垂直定向。 這會讓所有模塊一個接一個地往下放。
.container {
display: flex;
flex-direction: column;
}
現(xiàn)在我們需要讓主要部分和側(cè)邊欄挨著放。 由于 flex 容器通常是單向的,因此我們需要添加一個wrapper元素。
<div class="main-and-sidebar-wrapper">
<section class="main"></section>
<aside class="sidebar"></aside>
</div>
<footer></footer>
然后我們設置包裹層(wrapper)的display為:flex 并且flex-direction設置為相反的方向。
.main-and-sidebar-wrapper {
display: flex;
flex-direction: row;
}
最后一步是設置主要部分和邊欄的大小。 我們希望主要內(nèi)容是側(cè)邊欄大小的三倍,這對 flex 或百分比來說并不困難。
.main {
flex: 3;
margin-right: 60px;
}
.sidebar {
flex: 1;
}
你可以看到 flexbox 已經(jīng)做得很好了,但我們?nèi)匀恍枰喈敹嗟?CSS 屬性 + 一個額外的 HTML 元素。 讓我們看看 CSS 網(wǎng)格是如何工作的。
CSS Grid 方案
使用 CSS Grid有幾種不同的方法,我們將使用grid-template-areas,對于當下我們要完成的工作來說,它似乎是最適合的。
首先,我們將定義四個grid-area,每個網(wǎng)格區(qū)域一個:
<!-- Notice there isn't a wrapper this time -->
<section class="main"></section>
<aside class="sidebar"></aside>
<footer></footer>
header {
grid-area: header;
}
.main {
grid-area: main;
}
.sidebar {
grid-area: sidebar;
}
footer {
grid-area: footer;
}
然后我們可以建立我們的grid,并分配每個區(qū)域的位置。
下面的代碼一開始可能看起來相當復雜,但是一旦了解了網(wǎng)格系統(tǒng),就很容易理解了。
.container {
display: grid;
/* Define the size and number of columns in our grid.
The fr unit works similar to flex:
fr columns will share the free space in the row in proportion to their value.
We will have 2 columns - the first will be 3x the size of the second. */
grid-template-columns: 3fr 1fr;
/* Assign the grid areas we did earlier to specific places on the grid.
First row is all header.
Second row is shared between main and sidebar.
Last row is all footer. */
grid-template-areas:
"header header"
"main sidebar"
"footer footer";
/* The gutters between each grid cell will be 60 pixels. */
grid-gap: 60px;
}
就是這樣! 布局現(xiàn)在遵循開頭所說的結(jié)構(gòu),現(xiàn)在甚至不需要處理任何邊緣或填充。
挑戰(zhàn)2: 讓頁面響應迅速
Flexbox 方案
此步驟的執(zhí)行與前一個步驟緊密相連。 對于 flexbox 解決方案,我們將不得不改變wrapper的flex-direction ,并調(diào)整一些邊距。
@media (max-width: 600px) {
.main-and-sidebar-wrapper {
flex-direction: column;
}
.main {
margin-right: 0;
margin-bottom: 60px;
}
}
頁面真的很簡單,所以在媒體查詢中沒有太多需要修改的地方,但是在一個更復雜的布局中,將會有很多很多的東西需要重新定義。
CSS Grid 方案
因為我們已經(jīng)定義了grid-areas ,現(xiàn)在只需要在媒體查詢中對它們進行重新排序。我們可以使用相同的列設置。
@media (max-width: 600px) {
.container {
/* Realign the grid areas for a mobile layout. */
grid-template-areas:
"header header"
"main main"
"sidebar sidebar"
"footer footer";
}
}
或者,如果覺得有必要的話,我們可以從頭開始重新定義整個布局
@media (max-width: 600px) {
.container {
/* Redefine the grid into a single column layout. */
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
}
挑戰(zhàn)3: 對齊組件頭部
Flexbox 方案
頁眉包括一些導航鏈接和一個按鈕。 我們希望導航在左邊,按鈕在右邊。 導航中的鏈接必須彼此適當對齊。
<header>
<nav>
<li><a href="#"><h1>Logo</h1></a></li>
<li><a href="#">Link</a></li>
<li><a href="#">Link</a></li>
</nav>
<button>Button</button>
</header>
在一篇舊文章The Easiest Way To Make Responsive Headers中,我們已經(jīng)使用flexbox做了一個類似的布局,這個技巧非常簡單:
header {
display: flex;
justify-content: space-between;
}
現(xiàn)在導航列表和按鈕正確對齊。 剩下的就是讓<nav> 中的項水平居中。 在這里使用 display: inline-block 是最簡單的,但是既然我們要使用完全 flexbox,那么讓我們應用一個flexbox-only解決方案:
header nav {
display: flex;
align-items: baseline;
}
只有兩行! 一點也不差。 接下來讓我們看看 CSS Grid是如何處理它的。
CSS Grid 方案
為了分割導航和按鈕,我們必須設置header為display: grid ,并設置一個2列的網(wǎng)格。 另外還需要兩行額外的 CSS 來定位它們在各自的邊界上。
header{
display: grid;
grid-template-columns: 1fr 1fr;
}
header nav {
justify-self: start;
}
header button {
justify-self: end;
}
至于導航中的內(nèi)聯(lián)鏈接--其實我們使用css grid是無法非常精準實現(xiàn)的。 以下是我們的最佳嘗試:
這些鏈接是內(nèi)聯(lián)的,但是它們不能正確對齊,因為沒有像 flexbox 的 align-items 那樣的baseline選項。 我們還必須定義另一個子網(wǎng)格。
header nav {
display: grid;
grid-template-columns: auto 1fr 1fr;
align-items: end;
}
很明顯,CSS grid在布局的這一部分遇到了困難,但這并不奇怪——它的重點是調(diào)整容器,而不是容器內(nèi)的內(nèi)容。
總結(jié)
如果你已經(jīng)讀完了整篇文章,這個結(jié)論對你來說并不意外。 事實上是,沒有一個最優(yōu)的解決方案--flexbox和 CSS Grid擅長的東西不同,決定了使用場景不同,它們應該一起被使用,而不是作為競爭對手。
對于那些直接跳到文章結(jié)論的人(不用擔心,我也經(jīng)常這樣做)),這里有一個全文比較結(jié)論的摘要:
-
CSS grids對于構(gòu)建更大的圖片非常有用。 它們使管理頁面布局變得非常容易,甚至可以處理更多非傳統(tǒng)和不對稱的設計 -
Flexbox非常擅長對齊元素內(nèi)容,它可以用來定位設計中較小的細節(jié) - 使用
CSS grids進行2D 布局(行和列) -
Flexbox只在一個維度時(行或列)工作得更好 - 沒有理由只使用
CSS grids或只使用flexbox。 我們應該同時學習它們,并使用它們