
前言
很長一段時間, 我知道有flex這個布局方式, 但是始終沒有去學它. 3點原因:
- 感覺還比較新, 擔心兼容性不好.
- 普通的布局方式能滿足我的絕大多數需求.
- 好像蠻復雜的.
最近由於開發需要, 學習了下WeUI的實現, 發現里面大量使用了flex布局, 於是決定學習一下.
什么是flex
Flexbox Layout, 官方名為CSS Flexible Box Layout Module, 意為"彈性布局", 是CSS3中引入的一種更加靈活高效的布局/對齊/排序方式(還有一種更適合大型布局的網格布局CSS Grid Layout Module). flex是flexible的縮寫.
任何一個容器都可以指定為flex布局。
.box {
display: flex;
}
行內元素也可以使用flex布局。
.box {
display: inline-flex;
}
flex的基本概念

采用flex布局的元素被稱為flex容器 (flex container), 它的子元素即為flex元素 (flex item).
flex容器中包含兩個相互垂直的軸, 即主軸 (main axis)和副軸 (cross axis).
flex元素沿主軸從主軸起點 (main start)到主軸終點 (main end)依次排布.
如果flex容器包含多行flex元素, 則flex行 (flex lines)沿副軸從副軸起點 (cross start)到副軸終點 (cross end)依次排布.
單個flex元素占據的主軸空間叫做主軸長度 (main size), 占據的副軸空間叫做副軸長度 (cross size).
flex的兼容性

Getting Dicey With Flexbox中提到:
There's a popular myth floating around that flexbox isn't ready for prime time. Wrong! 93% of people are now running a browser that supports flexbox. That's better than the support for the HTML5
<video>element.
前一段時間同事做過video相關的開發, 踩到各種坑, 因此我知道video的支持不那么好, 特別是在Android上. 讓我驚奇的是flex竟然比video的支持更好?


從CanIUse的數據來看, flex的支持度是: 82.65% (支持) + 14.17% (部分支持) = 96.81%, 而video的支持度是: 92.48%. 瀏覽器對flex的支持好像並沒有特別好...
但是有微信的WeUI使用了flex布局, 我覺得在移動端flex應該還是支持度比較高的.
所以, 如果你是做移動端開發的, 可以優先考慮flex.
flex屬性
下面就開始介紹與flex布局相關的屬性. 以作用對象分為兩組, 第一組作用於flex容器, 第二組作用於flex元素.
注意: 以下屬性值都可以有initial(該屬性的默認值)和inherit(繼承自父元素), 本處省略.
用於flex容器的屬性
這類屬性有6種, 分別為:
| 屬性 | 含義 |
|---|---|
flex-direction |
主軸方向 |
flex-wrap |
換行樣式 |
flex-flow |
前兩個的簡寫形式 |
justify-content |
主軸對齊方式 |
align-items |
單行的副軸對齊方式 |
align-content |
多行的副軸對齊方式 |
注意:
- flex容器的
column-*屬性會失效. - flex容器無法擁有
::first-line和::first-letter虛元素.
flex-direction
| 含義 | 主軸方向 |
| 可選值 | row | row-reverse | column | column-reverse |
| 默認值 | row |
row |
direction為ltr時從左向右→, rtl時從右向左←. |
row-reverse |
direction為ltr時從右向左←, rtl時從左向右→. |
column |
從上到下↓. |
column-reverse |
從下到上↑. |
注意: row和row-reverse受到了direction屬性(默認值為ltr, 可改為rtl)的影響.
flex-wrap
| 含義 | 換行樣式 |
| 可選值 | nowrap | wrap | wrap-reverse |
| 默認值 | nowrap |
nowrap |
不換行 |
wrap |
換行. 行與行從上到下↓排布 |
wrap-reverse |
換行. 行與行從下到上↑排布 |
flex-flow
| 含義 | flex-direction和flex-wrap的簡寫形式 |
| 可選值 | flex-direction flex-wrap |
| 默認值 | row nowrap |
justify-content
| 含義 | 主軸對齊方式 |
| 可選值 | flex-start | flex-end | center | space-between | space-around |
| 默認值 | flex-start |

align-items
| 含義 | 單行的副軸對齊方式 |
| 可選值 | flex-start | flex-end | center | stretch | baseline |
| 默認值 | stretch |

align-content
| 含義 | 多行的副軸對齊方式 |
| 可選值 | stretch | flex-start | center | flex-end | space-between | space-around |
| 默認值 | stretch |
注意: 此屬性只在flex容器中有多行flex元素時才有作用.

用於flex元素的屬性
這類屬性有6種, 分別為:
| 屬性 | 含義 |
order |
排列順序 |
align-self |
flex元素的副軸對齊方式. 對應於flex容器的align-items. |
flex-grow |
放大比例 |
flex-shrink |
縮小比例 |
flex-basis |
初始大小 |
flex |
上面三個的簡寫形式 |
注意: flex元素的float, clear和vertical-align會失效.
order
| 含義 | 排列順序. 沿着主軸, flex元素按order的增序排列. |
| 可選值 | <integer> |
| 默認值 | 0 |

align-self
| 含義 | flex元素的副軸對齊方式. 對應於flex容器的align-items. |
| 可選值 | auto | stretch | center | flex-start | flex-end | baseline |
| 默認值 | auto |
當flex元素有父元素時, 它的align-self: auto即為父元素的align-items屬性; 否則(無父元素時), 相當於stretch.

flex-grow
| 含義 | 放大比例 |
| 可選值 | <number> (非負值) |
| 默認值 | 0 |
當有剩余空間時, flex元素會根據flex-grow按比例分配剩余空間.
默認值0代表, 即使有剩余空間, 該flex元素也不放大.

flex-shrink
| 含義 | 縮小比例 |
| 可選值 | <number> (非負值) |
| 默認值 | 1 |
當flex容器空間不足時, flex元素會根據flex-shrink按比例縮小.
flex-shrink為0則表示, 即使flex容器空間不足, 該flex元素也不縮小.

flex-basis
| 含義 | 初始大小 |
| 可選值 | auto|<length> (非負值) |
| 默認值 | auto |
flex-basis定義了分配剩余空間之前flex元素的初始大小, 可為長度值(如20%, 5rem等)或auto等關鍵詞.
flex-basis: auto表示, 以flex元素的主軸長度為flex-basis. 若flex元素的主軸長度也是auto, 則以flex元素內容(即所有子元素)的大小為flex-basis.
除了auto還有content, max-content, min-content和fit-content關鍵詞, 但是現在瀏覽器對它們的支持太少, 可以忽略.
flex
| 含義 | flex-grow, flex-shrink和flex-basis的簡寫形式 |
| 可選值 | none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ] |
| 默認值 | 0 1 auto |
(敲黑板) 同學們注意, 這里是重點!
這里的可選值我參照了W3C flexbox的寫法. 其中:
||用來分割兩個或多個選項, 從中選取一個或多個, 不限次序.|用來分割兩個或多個選項, 從中選取一個.[]只是用來分組的.?代表可選.
舉例來說, a | [ b || c ]包含的可能情況有a, b, c, b c, c b.
現在回過頭來再看none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ]就清晰多了.
注意, none是一個特殊值, 相當於0 0 auto.
另外, 如果flex中不指定:
flex-grow成員, 則flex-grow會被置為1.flex-shrink成員, 則flex-shrink會被置為1.flex-basis成員, 則flex-basis會被置為0.
注意: flex的初始值是0 1 auto, 即由每個flex因子本身的默認值組成(比方說flex-grow的默認值就是0).
但是, 如果利用flex設置了至少一個flex因子, 那么沒被設置的那些flex因子的默認值(按grow, shrink, basis的順序)分別是1 1 0.
我來舉幾個栗子.
/* 特殊值none */
flex: none; /* 相當於0 0 auto */
/* 單值,沒有單位的數字,是flex-grow */
flex: 2; /* 相當於 flex: 2 1 0 */
/* 單值,有單位的,寬、高,是flex-basis */
flex: 10em; /* 相當於 flex: 1 1 10em */
flex: 30px; /* 相當於 flex: 1 1 30px */
flex: auto; /* 相當於 flex: 1 1 auto */
flex: content; /* 相當於 flex: 1 1 content */
/* 兩個值:flex-grow flex-basis */
flex: 1 30px; /* 相當於 flex: 1 1 30px */
/* 兩個值:flex-grow flex-shrink */
flex: 2 2; /* 相當於 flex: 2 2 0 */
/* 三個值:flex-grow flex-shrink flex-basis */
flex: 2 2 10%;
W3C建議使用簡寫形式flex, 因為它可以方便地應對下面4種常見情況.
flex: initial |
即flex: 0 1 auto. 以auto方式計算flex-basis, 不可放大, 可縮小. |
flex: auto |
即flex: 1 1 auto. 以auto方式計算flex-basis, 可放大, 可縮小. |
flex: none |
即flex: 0 0 auto. 以auto方式計算flex-basis, 不可放大, 不可縮小. |
flex: <positive-number> |
即<positive-number> 1 0. flex-basis為0, 以<positive-number>比例增大, 以1的比例縮小. |
flex元素大小的計算方法
自此, 我們已經知道了flex-grow, flex-shrink和flex-basis的作用. 根據這三個值, 計算flex元素的大小只需三步:
第一步: 計算元素的flex-basis, 有兩種情況: 1. 具體的長度值, 或, 2.auto(即flex元素的大小). (這里忽略了content等目前支持還不完善的關鍵詞).
第二步: 計算剩余空間, 即剩余空間 = flex容器的內部空間 - flex元素flex-basis值的總和.
第三步: 按照flex因子(放大時為flex-grow; 縮小時為flex-shrink)分配剩余空間到每個元素. flex元素的最終大小 = flex-basis - flex-factor * 剩余空間.
舉個栗子.
假設flex容器的內部空間為200px, flex元素的大小的總和是160px. 看起來, 還有200 - 160 = 40px的剩余空間, 應該放大flex元素, 是不是? 不一定! 要看它們的flex-basis總和.
假設它們的flex-basis總和是300px, 那么剩余空間應該是300 - 200 = -100px. 此時剩余空間是負數, 應該以flex-shrink對每個flex元素在flex-basis的基礎上進行縮小.
下例中, 所有flex元素本身的大小為80px, 元素中為flex值.
你可以看到, 第一行的flex元素因為設置了flex-basis:auto, 所以它們的flex-basis就相當於元素大小, 即80px, 即flex-basis總和為160px, 不足容器的200px空間, 此時應該放大元素. 但又由於元素的flex-grow為0, 所以每個元素分配到0 * 40 = 0px的剩余空間, 即不放大.
第二行的flex元素設置了flex-basis:150px, 所以它們的flex-basis總和為300px, 超過了容器的200px空間, 故按照flex-shrink(比例為1:3)進行縮小. 由於剩余空間為-100px, 所以第一個元素應縮小25px變成125px, 第二個元素應縮小75px變成75px.
"絕對flex"和"相對flex"

絕對flex: 從0開始分配空間.
第一行中flex-basis為0, 表示每個flex元素的初始大小都視為0. 此時, 剩余空間就是"flex容器的大小".
相對flex: 從flex元素大小開始分配空間.
第二行中flex-basis為auto, 表示每個flex元素的初始大小都是它本身的大小. 此時, 剩余空間就是"flex容器的大小 - flex元素大小的總和".
結語
呃... flex的東西還是挺多的, 特別是flex因子相關的部分, 得花點兒時間理解.
但是, 我相信學flex是值得的, 誰用誰知道!

