开始根据林想的Web工坊大佬的建议路线学习CSS。
本文涉及内容会如下:
CSS flexible box layout - CSS | MDN
flex是内容驱动排列,而grid则是布局驱动排列。
📊 本节知识点重要度速览:
display: flex/ 双轴概念 /flex: 1/align-items/justify-content🔴 每天必写;
flex-direction/flex-wrap/flex-grow/flex-shrink/align-self🟡 常用;
flex-flow简写 /row-reversecolumn-reverse/ 初始值细节 🟢 知道就行;书写模式(writing modes)理论 ⚪ 了解即可。
The two axes of flexbox(Flexbox 的双轴模型)
Flexbox 被称为一维(one-dimensional)布局模型,它一次只在一个维度上进行布局,要么是一行(row),要么是一列(column)。这与 CSS Grid 的二维模型形成了核心区别:Grid 同时控制列和行,而 Flexbox 一次只处理一个方向。
这个”一维”是理解 Flexbox 所有行为的总钥匙。所有属性都是围绕”主轴”和”交叉轴”两个概念展开的,主轴决定元素往哪个方向排列,交叉轴决定另一个方向上如何对齐。
使用 Flexbox 时,需要始终在两个轴之间切换思维。主轴(main axis)由 flex-direction 属性定义,交叉轴(cross axis)垂直于主轴。
The main axis(主轴)
flex-direction 有四个可能的值:
row(默认):主轴沿行方向,从左到右(inline direction)row-reverse:主轴沿行方向,但从右到左column:主轴沿列方向,从上到下(block direction)column-reverse:主轴沿列方向,但从下到上
选择
row或row-reverse,主轴跑 inline 方向(文字书写的水平方向);选择column或column-reverse,主轴跑 block 方向(段落堆叠的垂直方向)。
The cross axis(交叉轴)
交叉轴永远垂直于主轴。所以:
- 如果主轴是
row/row-reverse→ 交叉轴沿列方向(垂直) - 如果主轴是
column/column-reverse→ 交叉轴沿行方向(水平)
可以这样记:主轴 = 排列方向,交叉轴 = 对齐方向。 主轴决定行或列内元素从左到右排列,谁在第一个、谁在第二个、间距怎么分(justify-content);交叉轴决定这一行/列里,若干个元素在垂直方向上怎么对齐,是全部贴顶(flex-start)、全部居中(center)、全部拉伸等高(stretch)、还是全部贴底(flex-end)。
Start and end lines(起止线:Flexbox 不假设书写方向)
Flexbox 的一个重要设计理念是不假设文档的书写方向。它不会默认”文字从左上方开始、从左往右排列、新行逐行往下”,而是支持所有书写模式(writing modes),与逻辑属性(logical properties)体系保持一致。
因此,Flexbox 用 start 和 end 代替 left 和 right:
- 如果
flex-direction: row且在英语环境下 → 主轴 start = 左,end = 右 - 如果
flex-direction: row且在阿拉伯语环境下 → 主轴 start = 右,end = 左 - 两种情况下,交叉轴的 start 都在顶部、end 都在底部(因为两种语言都是水平书写模式)
习惯用 start/end 而非 left/right 思考,不仅对 Flexbox 有用,对 CSS Grid 也是一样的,两者共享这套逻辑坐标系。
The flex container(Flex 容器与初始值)
将一个元素设为 display: flex,它就变成了 flex container,其直接子元素会立即变成 flex items。也可以用 inline-flex 创建行内级别的 flex 容器。
这里有一个值得停留的点:
display: flex创建的是全新的 flex formatting context,不是 BFC。上一篇文章我们提到 Flexbox 和 Grid 创建的是全新的格式化上下文,这正是 Flexbox “摆脱了 BFC 补丁链”的技术实现。在 flex 容器内部,float 会失效、margin 不会折叠、不需要 clear,BFC 那条线的所有历史问题,在这里全部清零。
初始值(Initial values)
一个刚创建的 flex 容器,子元素会按以下默认行为排列:
- 沿行方向排列,
flex-direction默认值为row - 从主轴 start 边缘开始
- 主轴上不伸长但可收缩,
flex-grow: 0(不伸长),flex-shrink: 1(可收缩) - 交叉轴上拉伸填满,
align-items默认值为stretch,高度不同的子元素会被拉成最高元素的高度 flex-basis: auto,元素的初始大小取width/height值,若也为auto则取内容大小- 不换行,
flex-wrap默认值为nowrap,子元素总宽度超过容器时直接溢出
综合效果就是:所有子元素在一行内排列,各自取内容大小,高度不同的会被拉成对齐,数量太多就直接溢出。这解释了为什么第一次写 display: flex 时,元素会自动排成一行但不会换行。
1
2
3
4
5
6
7
.box {
display: flex;
/* 等价于以下默认值都已生效:
flex-direction: row;
flex-wrap: nowrap;
align-items: stretch; */
}
Changing flex-direction(改变主轴方向)
通过 flex-direction 可以切换主轴的排列方向:
1
2
3
4
5
6
.box {
display: flex;
flex-direction: row-reverse; /* 仍然沿行方向,但从右到左 */
}
/* 其他可选值:row(默认)、column、column-reverse */
row-reverse 保持沿行排列,但 start 和 end 对调。column 让主轴切换到垂直方向,子元素从上到下堆叠。column-reverse 则是从下往上。
Multi-line flex containers with flex-wrap(多行 Flex 容器与换行)
虽然 Flexbox 是一维模型,但flex items 依然可以换行。不过这里有一个重要认知:每一行就是一个独立的 flex 容器。 任何空间分配都在每行内部独立计算,行与行之间互不参考。
1
2
3
4
5
6
7
8
9
.box {
display: flex;
width: 500px;
flex-wrap: wrap; /* 允许换行 */
}
.box > * {
width: 200px;
}
三个 200px 宽的 items 在一个 500px 宽的容器里:前两个刚好放下(400px),第三个被迫换到下一行。每一行内部的空间分配都是独立的。
如果设 flex-wrap: nowrap(初始值),三个 items 会尝试挤在一行内,由于 flex-shrink: 1,它们会收缩。如果收缩还放不下,就会溢出容器。
nowrap下的收缩行为是新手最常见的困惑:明明没设width: 200px,为什么元素还是会被挤小?答案就是flex-shrink的默认值为 1。但 flex-shrink 往往不是直接生效,它有一个隐藏的限制,即min-width。flex item 的 min-width 默认值是 auto,含义是”不能小于内容本身的最小尺寸”。
flex-shrink 设了等于白设会两种情况:
替换元素(img、video、iframe):固有宽度就是 min-width 的底,一张 800px 的图,flex-shrink: 999 也缩不动它。解法是给 img 加 max-width: 100%,让它自适应容器。
连续长单词或长数字:浏览器不会在单词中间断行,这个单词有多长,flex item 就至少有多宽,缩到这个宽度就停。解法是设 min-width: 0 强制取消内容下限,或配合 word-break: break-all 允许任意断行。
所以默认的 flex-shrink: 1 只是表达了内容元素能够被压缩的态度,实际能不能缩动,取决于内容的 min-width 的规定。
The flex-flow shorthand(flex-flow 简写)
flex-flow 是 flex-direction 和 flex-wrap 的组合简写:
1
2
3
4
.box {
display: flex;
flex-flow: row wrap; /* 第一个值:flex-direction,第二个值:flex-wrap */
}
Properties applied to flex items(作用于 Flex Items 的三个核心属性)
在 Flexbox 中,控制每个子元素在主轴上如何分配空间,靠三个属性:
flex-grow:有多少剩余空间时,这个 item 能分到多少flex-shrink:空间不够时,这个 item 要多快收缩flex-basis:这个 item 在分配/收缩之前的初始大小
这三个属性都作用在主轴方向。理解它们之前,需要先理解一个关键概念,可用空间(available space)。
Flexbox 的空间计算逻辑是:先算容器有多宽,再算所有 items 的 flex-basis 加起来占多少宽,差值就是”可用空间”(可正可负)。然后根据 flex-grow 或 flex-shrink 的比例去分配这个差值。改变这三个属性的值,本质上就是在改变”可用空间怎么分”。
举个例子:三个 100px 宽的 items 放在 500px 宽的容器里 → 占用 300px,剩余 200px 可用空间。如果什么都不设,这 200px 就会留在最后那个 item 的后面。
flex-basis(初始尺寸)
flex-basis 决定了 item 在分配空间之前的大小。默认值为 auto,浏览器会先看元素有没有显式的 width/height,有就用它;没有就用内容的自然尺寸。这就是为什么单纯设 display: flex 后,每个 item 只占内容所需的宽度,因为 flex-basis: auto,没设 width,就取内容大小。
flex-grow(伸长比例)
flex-grow 控制 item 如何吃掉可用空间。默认值为 0(不吃)。设为正整数后:
- 所有 item 的
flex-grow之和 = 总份数 - 每个 item 分到的额外空间 =
可用空间 × (该 item 的 flex-grow / 总份数)
1
2
3
4
5
6
/* 三个 items 均分 200px 可用空间,各得 66.67px */
.one, .two, .three { flex-grow: 1; }
/* one 分到 100px(2/4),其余各分到 50px(1/4) */
.one { flex-grow: 2; }
.two, .three { flex-grow: 1; }
flex-grow的值是比例,不是像素值。三个 items 都设flex-grow: 1和都设flex-grow: 100,效果完全一样,因为比例相同。
flex-shrink(收缩比例)
flex-shrink 控制空间不够时,item 如何收缩。默认值为 1。
- 值越大,收缩得越快(比兄弟们更快变小)
- item 最小可以缩到
min-content大小(内容不可再分的最小宽度)
flex-shrink 的值也是比例。以容器 600px、三个 item 各 300px 为例:
1
2
3
4
5
6
7
8
9
/* 三个 items 均分 300px 溢出空间,各缩 100px */
.one, .two, .three { flex-shrink: 1; }
/* 结果:各从 300px 变成 200px */
/* one 缩 150px(3/6),two 缩 100px(2/6),three 缩 50px(1/6) */
.one { flex-shrink: 3; }
.two { flex-shrink: 2; }
.three { flex-shrink: 1; }
/* 结果:one→150px, two→200px, three→250px */
flex-shrink的行为比flex-grow看起来更不一致,因为收缩过程要受min-content下限的影响,一个已经缩到内容最小宽度的 item 不能再缩,导致的收缩效果会转移到其他 item 身上。MDN 建议读到进阶文章再深入理解这一块算法。
The flex shorthand(flex 简写)
实际开发中几乎不会单独使用 flex-grow、flex-shrink 和 flex-basis,而是统一用 flex 简写:
1
2
/* flex: grow shrink basis */
.item { flex: 1 1 auto; } /* 可伸长、可收缩、初始尺寸为 auto */
三个值的顺序:flex-grow → flex-shrink → flex-basis。
还有一些预设值覆盖了大多数使用场景:
| 简写值 | 等价于 | 含义 |
|---|---|---|
flex: initial | flex: 0 1 auto | 不伸长,可收缩,取内容/显式尺寸 |
flex: auto | flex: 1 1 auto | 可伸长可收缩,取内容/显式尺寸 |
flex: none | flex: 0 0 auto | 完全僵硬,不能伸长不能收缩 |
flex: <number> | flex: <number> 1 0 | 最常用的 flex: 1 就是这个,basis 为 0 |
flex: 1会在无数教程里看到的写法。它等价于flex: 1 1 0,注意这里flex-basis: 0,意味着分配空间时可以完全忽略内容尺寸。这就是为什么flex: 1能让子元素”均分容器宽度”,而flex: auto(basis: auto)会让子元素先按内容占位再分余量。两者结果不同,根源就在flex-basis的差异。
Alignment, justification and distribution of free space between items(对齐、两端对齐与空间分配)
Flexbox 最强大的能力不是排列方向,而是对齐和对剩余空间的自动分配。当一组元素排在一行/一列里,容器还有多余空间时,怎么把这些空间”摆合理”,这就是 alignment 属性干的事。
align-items(交叉轴对齐)
align-items 控制所有 flex items 在交叉轴上的对齐方式:
stretch(默认):拉伸到容器交叉轴高度flex-start:靠近交叉轴的 start 边缘flex-end:靠近交叉轴的 end 边缘center:交叉轴居中baseline:按文字基线对齐
justify-content(主轴对齐/两端对齐)
justify-content 控制 flex items 在主轴上的分布方式,在有剩余空间时发挥作用:
flex-start(默认):items 靠主轴 start 边缘flex-end:items 靠主轴 end 边缘center:items 居中space-between:首尾贴边,中间均分间隙space-around:每个 item 两侧间隙相等(首尾 item 到容器边缘 = 间距的一半)space-evenly:所有间隙完全相等(包括首尾到容器边缘)
space-between是最常用的”两端对齐”,导航栏 logo 在最左、菜单在最右,就靠它。space-around和space-evenly的区别很微妙:前者是”每个 item 周围包一圈等宽空隙”(item 间距是首尾空隙的两倍),后者是”所有空隙宽度完全相同”。
虽然都是改变items的行为,但为什么justify-content的命名里却是content呢,这是因为命名是按 Grid 的四象限来设计的,Flexbox 只是继承了这套命名里的三个(
justify-content、align-items、align-content),justify-items在 Flexbox 里被闲置了。在 Grid 中,四个功能就非常符合自己的命名了:
1 2 3 4 5 justify-content: 所有列作为一个整体,在容器里靠左/居中/靠右 justify-items: 每个格子里的 item,在自己格子里靠左/居中/靠右 align-content: 所有行作为一个整体,在容器里靠上/居中/靠下 align-items: 每个格子里的 item,在自己格子里靠上/居中/靠下
align-self(单个 item 覆盖交叉轴对齐)
可以用 align-self 单独控制某个 flex item 在交叉轴上的对齐方式,覆盖容器的 align-items 设置。
1
2
3
4
5
6
7
.box {
display: flex;
align-items: center; /* 其他 items 居中 */
}
.special {
align-self: flex-start; /* 只有这个贴顶 */
}
小结:从 BFC 到 Flexbox 的思维转变
上一篇文章里我们说过,Flexbox 创建的是全新的 flex formatting context,BFC 的历史补丁链(float → clear → overflow: hidden → flow-root)在这里全部清零。
但 Flexbox 带给开发者的不只是”不用再打补丁”,而是一种思维方式的转变:
- BFC 思维:元素按照文档流自然堆叠,要用各种 hack 让它看起来像”布局”
- Flexbox 思维:定义一个容器,声明”主轴走行还是列”,剩下的由
align-items和justify-content自动搞定
核心要记住的只有一张表:主轴 = 排列方向(flex-direction + justify-content),交叉轴 = 对齐方向(align-items + align-self)。所有 Flexbox 属性都落在这两条轴上,理解了这一对轴,Flexbox 就不再是一堆散乱的属性,而是一套统一的逻辑体系。
📊 本节知识点重要度速览:
fr单位 /repeat()/gap/grid-template-columns/grid-auto-rows/ 隐式网格 🔴 每天必写;
minmax()/ 基于线的放置(grid-column/grid-row)/grid-template-areas🟡 常用;嵌套 Grid /
z-index层叠 🟢 知道就行;
subgrid/ 四个长写属性(grid-column-start等)⚪ 极少用到。
What is a grid?(什么是 Grid)
CSS Grid Layout 引入了二维(two-dimensional)网格系统,同时控制行和列。这是它与 Flexbox 最核心的区别:Flexbox 是一维的(每次只在一个方向上布局),Grid 是二维的(行列同时定义、元素可以跨越多个行和列)。
Grid 可以用来布局页面主体区域(header/sidebar/main/footer),也可以用来设计小型 UI 组件。Grid 规范提供以下核心能力:
- 固定和弹性轨道尺寸:像素、百分比,以及 Grid 专属的
fr单位 - 精确放置元素:通过行号、命名线、或区域名称将元素放到网格的任意位置
- 自动创建额外轨道:内容超出显式定义的网格时,自动生成新的行或列
- 对齐控制:控制元素在网格区域内的对齐方式,以及整个网格在容器内的对齐
- 元素重叠控制:多个元素可以放入同一个网格单元格,通过
z-index控制层叠
如果把 Flexbox 看作”沿着一条线排列”,Grid 就是”在一张纸上画格子”。这张纸的行和列同时存在,可以把元素放到任何一个格子里,也可以让它跨多行多列。
Grid container(Grid 容器)
和 Flexbox 一样,Grid 的起点是容器:
1
2
3
4
.wrapper {
display: grid;
}
/* 或 inline-grid 创建行内级别的 grid 容器 */
设了 display: grid 之后,所有直接子元素变为 grid items。在浏览器里可能看不到任何变化,因为 Grid 默认创建的是单列网格,所有 item 依然从上到下排列。打开 DevTools 的 Grid 检查器可以看到网格线的叠加层。
display: grid创建的是全新的 grid formatting context,与 Flexbox 一样,在 Grid 容器内部,float 失效、margin 不折叠、不需要 clear。BFC 的补丁链在这里同样彻底退场。
Grid tracks(网格轨道)
用 grid-template-columns 和 grid-template-rows 定义网格的轨道(track),也就是列和行。轨道是两条相邻网格线之间的空间。
1
2
3
4
5
.wrapper {
display: grid;
grid-template-columns: 200px 200px 200px;
/* 三列,每列 200px */
}
The fr unit(fr 单位)
Grid 引入了专属的长度单位 fr(fraction,份数),代表可用空间的一个比例份数:
1
2
3
4
5
6
7
8
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 三等分 */
}
.wrapper {
grid-template-columns: 2fr 1fr 1fr; /* 第一列占一半,后两列各 1/4 */
}
fr 的计算逻辑:先减掉所有固定尺寸(像素、百分比等),剩余空间按 fr 比例分配。
1
2
3
4
.wrapper {
grid-template-columns: 500px 1fr 2fr;
/* 500px 固定 → 剩余空间按 1:2 分配给第二列和第三列 */
}
fr是 Grid 的灵魂单位。它让”自适应列宽”从 Flexbox 里需要手动算百分比或flex-grow的间接操作,变成了直接声明比例。1fr 2fr就是”第一列占 1/3,第二列占 2/3”,直觉、直接、不用算。
repeat() notation(repeat 写法)
重复轨道可以用 repeat() 简化:
1
2
3
4
5
6
7
8
9
10
grid-template-columns: 1fr 1fr 1fr;
/* 等价于 */
grid-template-columns: repeat(3, 1fr);
/* 也可以和固定尺寸混合 */
grid-template-columns: 20px repeat(6, 1fr) 20px;
/* 甚至可以重复一段模式 */
grid-template-columns: repeat(5, 1fr 2fr);
/* 生成 10 列:1fr, 2fr, 1fr, 2fr, ... */
Implicit and explicit grids(隐式网格与显式网格)
这是 Grid 最独特的概念之一。当用 grid-template-columns 定义了 3 列,但子元素有 8 个时,Grid 会自动创建额外的行来容纳内容:
- 显式网格(explicit grid):由
grid-template-columns/grid-template-rows显式定义的行列 - 隐式网格(implicit grid):内容超出显式网格时,自动扩展出来的行或列
隐式轨道默认 auto 大小,既受内容影响,也受可用空间影响。可以用 grid-auto-rows / grid-auto-columns 控制隐式轨道的大小:
1
2
3
4
5
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 200px; /* 隐式行高 200px */
}
隐式网格是 Grid 设计最精妙的地方,不需要知道有多少行,只管定义列的规则,Grid 自己搞定内容溢出的部分。这和 Flexbox 的
flex-wrap有点像,但 Grid 可以精确控制”溢出出来的新行长什么样”。
minmax()(最小最大约束)
当希望行”至少 100px 高,但如果内容更多就自动撑开”时:
1
2
3
4
5
6
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(100px, auto);
/* 行高:最小 100px,最大 auto(撑开到内容所需) */
}
minmax() 接收两个参数:下限和上限。auto 作为上限时,轨道会增长到容纳内容的最大尺寸(max-content),同时还会和其他轨道共享容器内的剩余空间。
很明显minmax() 会是更常用的方式,具有一个基础的高度,但内容更多时也不会被截断。写死高度一般只在图片网格、仪表盘卡片这类内容高度可控的场景用。
Grid lines(网格线)
定义 Grid 时,我们定义的是轨道(tracks),不是线(lines)。但 Grid 会自动生成带编号的线,一个 3 列 2 行的网格有 4 条列线、3 条行线。
线的编号从 1 开始,方向遵循书写模式(左→右语言中 1 在左边,右→左语言中 1 在右边)。线也可以命名,用于更语义化的放置。
很容易能想到,如果我们不对网格线进行设置,items的摆放方式当然会按照fr的换分方式进行,但我们也需要清楚,fr本质划分的是轨道尺寸(track size),而我们可以让元素进行跨行的放置,也就是占用多个轨道。而我们通过对轨道线的操作可以实现这点,
Positioning items against lines(基于线的元素放置)
放置元素时,定位的是线,不是轨道:
1
2
3
4
5
6
.box1 {
grid-column-start: 1;
grid-column-end: 4; /* 从列线 1 跨越到列线 4 = 占满 3 列 */
grid-row-start: 1;
grid-row-end: 3; /* 从行线 1 到行线 3 = 占 2 行 */
}
注意:3 列网格有 4 条列线,所以 grid-column-end: 4 才到最右边那条线。
Line-positioning shorthands(简写)
1
2
3
4
5
6
7
8
9
10
/* 等价于上面的四个属性 */
.box1 {
grid-column: 1 / 4; /* start / end */
grid-row: 1 / 3;
}
.box2 {
grid-column: 1; /* 只跨一列,end 可省略 */
grid-row: 3 / 5;
}
/ 左边是 start 线号,右边是 end 线号。跨一个轨道时可以省略 end。
Grid cells(网格单元格)
网格单元格是 Grid 的最小单位,概念上类似 HTML 表格的 <td>。定义一个 Grid 后,子元素默认每个占一个单元格。
Grid areas(网格区域)
元素可以跨多行多列,形成一个网格区域(grid area)。一个关键约束:网格区域必须是矩形,不能创建 L 形区域。
Gutters(间距)
用 column-gap、row-gap 或简写 gap 设置列/行间距:
1
2
3
4
5
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px 1em; /* row-gap column-gap */
}
间距空间的优先级:gap 空间在
fr分配之前先扣除。也就是说,1fr 1fr 1fr+gap: 10px→ 容器宽度先减去 2×10px 的总间隙,剩余空间再三等分。gap 在尺寸计算上等价于一条固定宽的轨道,但gap 里不能放东西,它是一条粗的透明线。
Nesting grids(嵌套 Grid)
Grid item 可以变成 Grid container,形成嵌套网格。不过默认情况下,嵌套网格和父网格没有任何关系,线不对齐、gap 不继承。
1
2
3
4
5
6
7
8
9
10
11
12
/* 父元素:先声明 Grid,分出列 */
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr); /* 4 列 */
}
/* 子元素:在父 Grid 里跨 3 列,同时自己也是 Grid 容器 */
.box1 {
grid-column: 1 / 4; /* 靠父 Grid 的列线定位 */
display: grid; /* 自己开一套新的 Grid */
grid-template-columns: repeat(3, 1fr); /* 自己的子元素 3 列 */
}
Subgrid(子网格)
subgrid 让嵌套网格继承父网格的轨道定义:
1
2
3
4
5
.box1 {
grid-column: 1 / 4;
display: grid;
grid-template-columns: subgrid; /* 使用父网格的列线 */
}
subgrid是 Grid Level 2 的特性,解决的痛点是”嵌套网格的列线和父网格对不齐”。在实际项目中,如果在一个卡片网格里嵌套了另一个子卡片网格,想让它们的列宽完美对齐,就需要subgrid。不过浏览器兼容性要留意,目前主流浏览器基本都支持了。
Layering items with z-index(用 z-index 控制层叠)
Grid 的一个独特能力是:多个元素可以放入同一个单元格,自然形成重叠。通过 z-index 可以控制谁在上谁在下,和定位元素的层叠逻辑一样。
1
2
3
4
5
6
7
8
9
10
.box1 {
grid-column: 1 / 4;
grid-row: 1 / 3;
z-index: 2; /* 在上层 */
}
.box2 {
grid-column: 1;
grid-row: 2 / 4;
z-index: 1; /* 在下层 */
}
小结:Flexbox 和 Grid 的互补
学完 Flexbox 又学 Grid,最容易产生的疑问是:”到底用哪个?”
答案不是二选一,是组合使用。
| Flexbox | Grid | |
|---|---|---|
| 维度 | 一维(行或列) | 二维(行列同时) |
| 核心理念 | 沿一个轴排列 + 对齐 | 先画格、再放元素 |
| 内容驱动 vs 布局驱动 | 内容决定排列(flex-basis: auto) | 网格先定义、内容再填入 |
| 适用场景 | 导航栏、按钮组、表单行、列表 | 页面骨架(header/sidebar/main/footer)、卡片网格、仪表盘 |
| 关键单位 | flex-grow 比例 | fr 比例 + minmax() |
一个简单判断法则:“我是不是要知道每一列有多宽、每一行有多高”,如果是,用 Grid;“我只关心元素沿一个方向怎么排列和对齐”,用 Flexbox。实际项目中,可以用 Grid 搭页面骨架,用 Flexbox 做组件内部的排列。
容器属性(写在父元素上)
| Flexbox | Grid | 作用 |
|---|---|---|
display: flex | display: grid | 开启布局上下文 |
flex-direction | grid-template-columns/rows | 主轴方向 / 轨道划分 |
flex-wrap | grid-auto-rows/columns | 换行 / 隐式轨道 |
justify-content | justify-content | 整组在主轴上的位置 |
align-items | align-items | 整组在交叉轴上的位置 |
align-content | align-content | 多行分布 |
gap | gap | 间距 |
子项属性(写在子元素上)
| Flexbox | Grid | 作用 |
|---|---|---|
flex-grow | — | 伸长比例 |
flex-shrink | — | 收缩比例 |
flex-basis | — | 起始尺寸 |
align-self | align-self | 自己单独的对齐 |
order | order | 排列顺序 |
| — | grid-column | 跨列 |
| — | grid-row | 跨行 |
| — | grid-area | 占区域 |