还记得那些年被 CSS 布局折磨的日子吗?float: left
配合 clear: both
,各种奇技淫巧只为让元素乖乖待在该待的地方。好在现代 CSS 给了我们两个布局神器:Flex 和 Grid。
如果你已经在用这两个布局方式,但偶尔还是会被轴向搞混,那这篇复习笔记正好适合你。我们会从最基础的概念开始,到实际的使用技巧,再到 Tailwind 的原子类封装,最后总结一些实战心得。
准备好了吗?复习开始!
Flex
Flex 被翻译为弹性布局,看名字就知道,主打一个弹 。(此处应有音效~ Duang~)
弹
一个弹性盒子里的内容我们只需要设置部分的宽度,其他没有设置的就能自如调整自己宽度,填满盒子。
最常见的需求写成代码如下:
html
<div class="fixed-flex">
<div class="side">侧边栏</div>
<div class="main">主内容</div>
</div>
<style>
.fixed-flex {
display: flex;
}
.side {
width: 200px;
background: #ffecb3;
}
.main {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
background: #fff9c4;
}
</style>
.side
宽度固定 200px
,对于 .main
:
- 宽度不设置:
flex-basis: 0%
- 有多余空间时会膨胀:
flex-grow: 1
- 空间不足时收缩:
flex-shrink: 1
以上 3 个值可以简写成一个 flex: 1
,不过新手不推荐用除了 flex: 1
之外的 flex
属性,因为这个属性的语法有一点复杂。
换行
有时候你确实不想只填满一行,Flex 也给你换行的选项。
html
<div class="wrap-layout">
<div>Akira</div>
<div>Astro Boy</div>
<div>Cowboy Bebop</div>
<div>Dragon Ball Z</div>
<div>Fullmetal Alchemist</div>
<div>Ghost in the Shell</div>
<div>Mobile Suit Gundam</div>
<div>Neon Genesis Evangelion</div>
<div>Princess Mononoke</div>
<div>Sailor Moon</div>
</div>
<style>
.wrap-layout {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.wrap-layout div {
width: auto;
background: #c8e6c9;
padding: 10px;
text-align: center;
}
</style>
默认情况下,Flex 布局只有单行,元素位置不够会被自动挤压,添加 flex-wrap: wrap;
后,就能保持其宽度自动换行。
交叉轴
Flexbox 的设计理念是一维布局,它要么是行(row),要么是列(column)。这意味着所有子元素都会沿着主轴(main axis) 排成一条线。但是换行之后就不可避免地成为二维布局,产生了交叉轴(cross axis) 的概念。
默认情况下,横着的是主轴,换行就产生了垂直于主轴的交叉轴。
与主轴有关的 CSS 属性带有 justify 前缀:
justify-content
与交叉轴有关的 CSS 属性带有 align 前缀:
align-content
align-items
align-self
align-content
控制的是多行的情况下 ,行与行之间的排布,你不用 wrap
的话这个属性是不会生效的;align-items
控制的是单行内元素的排布。
排列方向
我们可以通过 flex-direction: column;
切换主轴方向。
html
<div class="column-layout">
<div>上</div>
<div>中</div>
<div>下</div>
</div>
<style>
.column-layout {
display: flex;
flex-direction: column;
gap: 10px;
}
</style>
十分简单!我们只需要一句 flex-direction: column;
就能把主轴从水平改为垂直,麻烦的它会给你带来一些思维负担。
上述所说的主轴、交叉轴、justify、align、换行概念都不变,只需要你把主轴换成垂直重新理解就好了,这不难,只是需要时间适应。
居中
Flex 拥有新时代最方便的居中布局的方法,justify-content: center;
和 align-items: center;
2015 年就在干前端的老家伙们仍记得,当年还需要记各种 CSS 居中 hack,现在浏览器跟上了,大部分时间都只需要使用 Flex 就能解决问题。
正如上面所说,justify-content
是主轴元素间的分布,align-items
是交叉轴一维元素的分布,结合起来就是在 Flex 盒子内完全居中。
html
<div class="center">
<p>居中内容</p>
</div>
<style>
.center {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
background: #f0f0f0;
}
</style>
这时候有朋友就要提问了,用 align-content
从行的视角居中不可以吗?也是可以的,不过正如上面所说,必须设置 wrap
,align-content
才能生效,所以得多加一行:
css
.center {
display: flex;
justify-content: center;
flex-wrap: wrap;
align-content: center;
height: 200px;
background: #f0f0f0;
}
你可以在这个互动学习网站玩玩 Flex 的 wrap
、align
和 justify
具体属性对元素布局的影响。
Bonus :Flex 父元素被撑爆了怎么办?当你设置了文字不换行之后,Flex 父元素经常会出现被撑爆的情况,这时候记得设置 overflow
属性。我觉得这里挺奇怪的,如果布局是垂直的时候,我会很容易想到设置 overflow
,但是使用 Flex 之后布局变成水平了,我就老忘记设置 😂
Grid
Grid 布局就是一个画格子的游戏,一个例子让我们看看格子怎么画:
css
.container {
display: grid;
grid-template-columns: 100px 200px auto;
grid-template-rows: 50px auto 50px;
}
某种程度上 Grid 比 Flex 简单,因为基本不用切换主轴和交叉轴的概念,不容易记混。对于 Grid 我们只要知道行列怎么设置就行了。
grid-template-columns
设置 Grid 的列,100px 200px auto
的含义就是第一列 100px,第二列 200px,第三列自适应。
grid-template-rows
设置 Grid 的行,50px auto 50px
的含义就是第一行 50px,第二行自适应,第三行 50px。
还有一个十分实用的单位 fr
,代表弹性比例(剩余空间的分数单位),举个例子:
css
.container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 1fr 1fr 1fr */
}
这样就是把列分成均等的 3 份。
对齐
Flexbox 的子元素总是在主轴上紧密地排列在一起,没有多余空间可以用来让单个子元素进行单独的对齐,所以 Flex 不存在 justify-items
。整个主轴上的对齐是由父容器的 justify-content
属性来统一控制的。它决定了所有子元素作为一个整体如何沿着主轴进行分布和对齐。
与之对比,Grid 就是画格子,格子画完之后,子元素在里面各自精彩,自然可以用 justify-items
。
命名网格
html
<div class="grid-container">
<header class="header">头部</header>
<nav class="nav">导航</nav>
<main class="content1">内容1</main>
<main class="content2">内容2</main>
<footer class="footer">底部</footer>
</div>
<style>
.grid-container {
display: grid;
grid-template-areas:
"header header header"
"nav content1 content1"
"nav content2 content2"
"footer footer footer";
grid-template-columns: 150px 1fr 1fr;
grid-template-rows: 100px 1fr 1fr 60px;
gap: 10px;
height: 90vh;
}
.header {
grid-area: header;
background-color: #f0c3a2;
}
.nav {
grid-area: nav;
background-color: #a2d2ff;
}
.content1 {
grid-area: content1;
background-color: #b7efc5;
}
.content2 {
grid-area: content2;
background-color: #b7efc5;
}
.footer {
grid-area: footer;
background-color: #f2e293;
}
</style>
不得不说这玩意有点好玩,在配置好行列宽高后,通过文字"画"出页面结构:
css
"header header header"
"nav content1 content1"
"nav content2 content2"
"footer footer footer"
网格跨越
除了通过 grid-template-areas
"画图",你还可以通过网格跨越设计页面元素。
css
.grid-container {
display: grid;
grid-template-columns: 150px 1fr 1fr;
grid-template-rows: 100px 1fr 1fr 60px;
gap: 10px;
height: 90vh;
}
.header {
grid-column: 1 / 4;
grid-row: 1 / 2;
background-color: #f0c3a2;
}
.nav {
grid-column: 1 / 2;
grid-row: 2 / 4;
background-color: #a2d2ff;
}
.content1 {
grid-column: 2 / 4;
grid-row: 2 / 3;
background-color: #b7efc5;
}
.content2 {
grid-column: 2 / 4;
grid-row: 3 / 4;
background-color: #b7efc5;
}
.footer {
grid-column: 1 / 4;
grid-row: 4 / 5;
background-color: #f2e293;
}
grid-column
后面的数字表示从第 N 个竖线到第 M 个竖线中间的内容,这或许有点抽象,为什么要用竖线而不是第 N 列 这样的说法?
其实使用第 N 列的写法也有,例如:
css
.header {
grid-column: 1 / span 3;
grid-row: 1 / span 1;
background-color: #f0c3a2;
}
如果你要控制某个区间跨度,还是必须要以数字开始,再加 span 数字
,1 / span 3
代表从第 1 条竖线开始,跨度 3 列。
直接写 grid-column: span 3;
只能代表 3 列,不能控制开始位置。
对齐
Grid 也有 align-
和 justify-
系列的对齐系统,它们的效果也与 Flex 里提到的类似。你可以在这个互动学习网站玩玩 Grid 的 align
和 justify
具体属性对元素布局的影响。
place-content
是 CSS 中的一个简写属性,它同时设置了 align-content
和 justify-content
这两个属性。它主要用于 Grid 布局,毕竟 Flex 布局比较少将这两个属性同时设置。举个例子:
css
place-content: center;
place-content: end space-between;
只填一个值的话同时应用到水平垂直,填两个值的话顺序是 place-content: <align-content> <justify-content>;
。
有 content
那有没有 items
呢?有的,朋友,如你所猜,就是 place-items
,使用方法也是同理可得,就不多赘述了。
Grid 的浏览器支持比 Flex 差一点,不过仍然看好未来,现在用户更新浏览器应该是越来越容易了,保留旧浏览器的情况会比以前大大减少。
Tailwind
Tailwind 把常用的 Flex 与 Grid API 封装为原子类,直接在 class 中组合即可。某种程度上来说,直接从 Tailwind 学习 Flex 布局还好学一点,不过如果你是 Tailwind 的反对者,大可以跳过这一节。
下面用类名重现上文的关键示例:
Flex 基础
- 容器与方向:
txt
flex
flex flex-col
flex-row-reverse
flex-col-reverse
- 间距与换行:
txt
gap-4
gap-x-6 gap-y-2
flex-wrap
flex-nowrap
Flex 对齐
- 主轴/交叉轴(容器):
txt
flex justify-between items-center
flex justify-center items-stretch
- 多行内容分布(容器,需要 flex-wrap 生效时才有体感):
txt
flex flex-wrap content-between
flex flex-wrap content-center
- 单个子项对齐(子项):
txt
self-start
self-center
self-end
self-stretch
Flex 子项尺寸与顺序(子项)
- 占比/伸缩/基础宽高:
txt
flex-1
grow
shrink-0
basis-40
Grid 基础
- 容器与网格轨道:
txt
grid
grid-cols-3
grid-rows-4
- 使用自定义列/行尺寸(对应上文 150px / 1fr / 1fr 与 100px / 1fr / 1fr / 60px):
txt
grid grid-cols-[150px_1fr_1fr] grid-rows-[100px_1fr_1fr_60px] gap-2 h-[90vh]
用跨越重现上文布局
Tailwind 原生未提供 grid-template-areas
的原子类,上文示例可用 col-start/col-end 与 row-start/row-end 组合实现:
- Header(跨 3 列,1 行):
txt
col-start-1 col-end-4 row-start-1 row-end-2
- Nav(第 1 列,从第 2 行跨到第 4 行):
txt
col-start-1 col-end-2 row-start-2 row-end-4
- Content1(第 2-3 列,第 2 行):
txt
col-start-2 col-end-4 row-start-2 row-end-3
- Content2(第 2-3 列,第 3 行):
txt
col-start-2 col-end-4 row-start-3 row-end-4
- Footer(跨 3 列,1 行):
txt
col-start-1 col-end-4 row-start-4 row-end-5
同样可以用简写:
txt
col-span-3 row-span-1
(在确定起始线的情况下用 start/end;仅控制跨度时可用 col-span-/row-span-。)
Grid 对齐
- 同时设置 align-content 与 justify-content(容器):
txt
place-content-center
place-content-between
- 同时设置 align-items 与 justify-items(容器):
txt
place-items-center
- 仅水平/仅垂直(容器):
txt
justify-items-start
align-items-end
- 自动放置方向与紧密填充:
txt
grid-flow-col
grid-flow-row-dense
Takeaways
- Flex 更适合一维 分布与对齐,Grid 更适合二维网格与跨行跨列;实际项目里常用"外层 Grid 划区,内层 Flex 排内容"的混搭策略。
- 牢记轴心:
justify-*
作用在主轴,align-*
作用在交叉轴;切换为 column 后,两者随主轴一并"旋转"。 - 换行的影响:只有设置了
flex-wrap
时,align-content
才会生效;单行关注align-items/align-self
,多行再谈align-content
。 - Flex 子项尺寸心法:固定 + 自适应 = 固定宽度 + flex: 1(等价 grow-1 shrink-1 basis-0%);除 flex: 1 外,新手谨慎使用更复杂的 flex 简写。
- Grid 的核心是"画格子":grid-template-columns/rows 配合 fr 单位定义轨道;跨区用 grid-column/grid-row(如 1 / 4 或 1 / span 3);也可用 template-areas 直观命名布局。
- 对齐总览:Grid 还支持
justify-items/align-items
,以及place-content/place-items
的简写组合,既能控制"内容整体",也能控制"单元格内"。 - 兼容性与实践:Grid 支持略逊于 Flex,但现代浏览器基本可用;旧环境保守选 Flex,新项目优先考虑 Grid + Flex 混搭。
推荐阅读: