flex布局全解析

114 阅读9分钟

前言

很长一段时间, 我知道有flex这个布局方式, 但是始终没有去学它. 3点原因:

  1. 感觉还比较新, 担心兼容性不好.
  2. 普通的布局方式能满足我的绝大多数需求.
  3. 好像蛮复杂的.

最近由于开发需要, 学习了下WeUI的实现, 发现里面大量使用了flex布局, 于是决定学习一下.

什么是flex

Flexbox Layout, 官方名为CSS Flexible Box Layout Module, 意为"弹性布局", 是CSS3中引入的一种更加灵活高效的布局/对齐/排序方式(还有一种更适合大型布局的网格布局CSS Grid Layout Module). flexflexible的缩写.

任何一个容器都可以指定为flex布局。

.box {
display: flex;
}

行内元素也可以使用flex布局。

.box {
display: inline-flex;
}

flex的基本概念

Flex Container and Item

采用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的兼容性

Browser Support

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的支持更好?

Can I use flex

Can I use 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

含义主轴方向
可选值`rowrow-reversecolumncolumn-reverse`
默认值row
rowdirectionltr时从左向右→, rtl时从右向左←.
row-reversedirectionltr时从右向左←, rtl时从左向右→.
column从上到下↓.
column-reverse从下到上↑.

注意: rowrow-reverse受到了direction属性(默认值为ltr, 可改为rtl)的影响.

flex-wrap

含义换行样式
可选值`nowrapwrapwrap-reverse`
默认值nowrap
nowrap不换行
wrap换行. 行与行从上到下↓排布
wrap-reverse换行. 行与行从下到上↑排布

flex-flow

含义flex-directionflex-wrap的简写形式
可选值flex-direction flex-wrap
默认值row nowrap

justify-content

含义主轴对齐方式
可选值`flex-startflex-endcenterspace-betweenspace-around`
默认值flex-start

justify-content

align-items

含义单行的副轴对齐方式
可选值`flex-startflex-endcenterstretchbaseline`
默认值stretch

align-items

align-content

含义多行的副轴对齐方式
可选值`stretchflex-startcenterflex-endspace-betweenspace-around`
默认值stretch

注意: 此属性只在flex容器中有多行flex元素时才有作用.

align-content

用于flex元素的属性

这类属性有6种, 分别为:

属性含义
order排列顺序
align-selfflex元素的副轴对齐方式. 对应于flex容器的align-items.
flex-grow放大比例
flex-shrink缩小比例
flex-basis初始大小
flex上面三个的简写形式

注意: flex元素的floatclearvertical-align会失效.

order

含义排列顺序. 沿着主轴, flex元素按order增序排列.
可选值<integer>
默认值0

order

align-self

含义flex元素的副轴对齐方式. 对应于flex容器的align-items.
可选值`autostretchcenterflex-startflex-endbaseline`
默认值auto

当flex元素有父元素时, 它的align-self: auto即为父元素的align-items属性; 否则(无父元素时), 相当于stretch.

align-self

flex-grow

含义放大比例
可选值<number> (非负值)
默认值0

当有剩余空间时, flex元素会根据flex-grow按比例分配剩余空间.

默认值0代表, 即使有剩余空间, 该flex元素也不放大.

flex-grow

flex-shrink

含义缩小比例
可选值<number> (非负值)
默认值1

当flex容器空间不足时, flex元素会根据flex-shrink按比例缩小.

flex-shrink0则表示, 即使flex容器空间不足, 该flex元素也不缩小.

flex-shrink

flex-basis

含义初始大小
可选值`auto` (非负值)
默认值auto

flex-basis定义了分配剩余空间之前flex元素的初始大小, 可为长度值(如20%5rem等)或auto等关键词.

flex-basis: auto表示, 以flex元素的主轴长度flex-basis. 若flex元素的主轴长度也是auto, 则以flex元素内容(即所有子元素)的大小为flex-basis.

除了auto还有contentmax-contentmin-contentfit-content关键词, 但是现在浏览器对它们的支持太少, 可以忽略.

flex

含义flex-growflex-shrinkflex-basis的简写形式
可选值`none[ <‘flex-grow’> <‘flex-shrink’>?<‘flex-basis’> ]`
默认值0 1 auto

(敲黑板) 同学们注意, 这里是重点!

这里的可选值我参照了W3C flexbox的写法. 其中:

  • ||用来分割两个或多个选项, 从中选取一个或多个, 不限次序.
  • |用来分割两个或多个选项, 从中选取一个.
  • []只是用来分组的.
  • ?代表可选.

举例来说, a | [ b || c ]包含的可能情况有abcb cc 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: initialflex: 0 1 auto. 以auto方式计算flex-basis可放大, 可缩小.
flex: autoflex: 1 1 auto. 以auto方式计算flex-basis, 可放大, 可缩小.
flex: noneflex: 0 0 auto. 以auto方式计算flex-basis可放大, 可缩小.
flex: <positive-number><positive-number> 1 0flex-basis0, 以<positive-number>比例增大, 以1的比例缩小.

flex元素大小的计算方法

自此, 我们已经知道了flex-growflex-shrinkflex-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值.

200px

0 1 auto

0 3 auto

0 1 150px

0 3 150px

125px

75px

你可以看到, 第一行的flex元素因为设置了flex-basis:auto, 所以它们的flex-basis就相当于元素大小, 即80px, 即flex-basis总和为160px, 不足容器的200px空间, 此时应该放大元素. 但又由于元素的flex-grow0, 所以每个元素分配到0 * 40 = 0px的剩余空间, 即不放大.

第二行的flex元素设置了flex-basis:150px, 所以它们的flex-basis总和为300px, 超过了容器的200px空间, 故按照flex-shrink(比例为1:3)进行缩小. 由于剩余空间为-100px, 所以第一个元素应缩小25px变成125px, 第二个元素应缩小75px变成75px.

"绝对flex"和"相对flex"

Relative vs Absolute flex-basis

绝对flex: 从0开始分配空间.

第一行中flex-basis0, 表示每个flex元素的初始大小都视为0. 此时, 剩余空间就是"flex容器的大小".

相对flex: 从flex元素大小开始分配空间.

第二行中flex-basisauto, 表示每个flex元素的初始大小都是它本身的大小. 此时, 剩余空间就是"flex容器的大小 - flex元素大小的总和".

结语

呃... flex的东西还是挺多的, 特别是flex因子相关的部分, 得花点儿时间理解.

但是, 我相信学flex是值得的, 谁用谁知道!

参考

  1. Flex 布局教程: 语法篇

  2. A Visual Guide to CSS3 Flexbox Properties

  3. A Complete Guide to Flexbox

  4. Getting Dicey With Flexbox: 用flex画骰子 flex dice

  5. W3C: CSS Flexible Box Layout Module Level 1