CSS 三栏布局怎么写:Flex、Grid 完整方案与 BFC 区别
左右固定宽度、中间自适应,是前端面试常见的三栏布局题。初学者通常知道 display: flex,却容易漏掉左右栏尺寸、中间栏伸缩和长内容溢出;还可能把 Flex、Grid 与 BFC 混为一谈。本文给出 Flex、Grid 两套完整模板,并说明三种格式化上下文的边界。
本文基于现有 HTML 和 CSS 笔记进行静态分析,浏览器运行未验证。
三栏布局的目标是什么
典型三栏布局需要满足:
- 左栏固定宽度;
- 右栏固定宽度;
- 中间栏占据剩余空间;
- 中间内容较长时不能随意撑破容器。
语义结构可以使用两个 aside 和一个 main:
html
<div class="layout">
<aside class="sidebar left">左侧栏</aside>
<main class="main">主内容区域</main>
<aside class="sidebar right">右侧栏</aside>
</div>
仅有三个元素还不够,关键是 CSS 如何分配宽度。
Flex 实现左右固定、中间自适应
完整 Flex 方案如下:
css
.layout {
display: flex;
}
.sidebar {
flex: 0 0 200px;
}
.main {
flex: 1;
min-width: 0;
}
flex: 0 0 200px 做了什么
它可以拆成:
css
.sidebar {
flex-grow: 0;
flex-shrink: 0;
flex-basis: 200px;
}
含义是左右栏不放大、不缩小,基准宽度为 200px。
中间栏为什么设置 flex: 1
flex: 1 让中间栏参与剩余空间分配,因此左右栏占据固定空间后,其余宽度交给中间栏。
为什么还要 min-width: 0
Flex item 的默认最小尺寸可能受内容影响。当中间栏包含长文本或不可换行内容时,它可能拒绝继续收缩,从而撑破布局。
css
.main {
flex: 1;
min-width: 0;
}
min-width: 0 允许中间栏缩小到内容固有宽度以下。若内容仍可能溢出,还应根据需求设置换行或滚动策略。
Grid 实现三栏布局
Grid 可以直接声明三列轨道:
css
.layout {
display: grid;
grid-template-columns: 200px minmax(0, 1fr) 200px;
}
三列分别表示:
- 左栏
200px; - 中间栏占据剩余空间;
- 右栏
200px。
中间列使用 minmax(0, 1fr),而不只是 1fr,是为了明确允许轨道最小值降到 0,降低长内容撑破布局的风险。
三栏虽然是一维问题,但 Flex 和 Grid 都能完成:
| 对比项 | Flex | Grid |
|---|---|---|
| 核心模型 | 沿主轴分配空间 | 定义行列轨道 |
| 三栏写法 | 子项分别设置伸缩 | 容器直接声明三列 |
| 适用场景 | 一维排列与动态伸缩 | 列尺寸关系明确或需要二维布局 |
| 中间列防溢出 | min-width: 0 |
minmax(0, 1fr) |
现有 Flex 示例为什么还没完成
常见的初始代码只有:
css
.layout {
display: flex;
}
这只建立了 Flex Formatting Context,让三个直接子元素成为 flex items,但没有表达:
- 左右栏到底多宽;
- 左右栏是否允许收缩;
- 中间栏是否吸收剩余空间;
- 长内容如何处理。
因此,"开启 Flex"不等于"完成三栏布局"。
BFC 到底是什么
BFC 全称 Block Formatting Context,通常译为块级格式化上下文 。它是一种布局环境,不是某个具体元素,也不等于 display: block。
BFC 常见作用包括:
- 包含内部浮动元素;
- 隔离部分外边距折叠;
- 避免块盒与浮动元素重叠。
现代代码如果只是想显式创建 BFC,可以使用:
css
.container {
display: flow-root;
}
过去常见的方式是:
css
.container {
overflow: hidden;
}
但 overflow: hidden 还会裁剪溢出内容。如果目的只是创建 BFC,flow-root 的意图通常更明确。
哪些情况会创建 BFC
创建条件必须写完整,不能只背属性名:
- 浮动元素,即
float不为none; - 绝对定位或固定定位元素;
overflow的计算值不为visible或clip的块级元素;display: flow-root;inline-block等特定显示类型。
需要注意:
- 普通
position: relative不会创建 BFC; float: none不会创建 BFC;- 不能只说"写了
overflow就有 BFC"。
Flex、Grid 和 BFC 是什么关系
它们都是格式化上下文,但内部布局算法不同:
| 名称 | 常见创建方式 | 主要负责什么 |
|---|---|---|
| BFC | display: flow-root 等 |
块级盒布局与隔离 |
| Flex Formatting Context | display: flex |
flex items 的一维布局 |
| Grid Formatting Context | display: grid |
grid items 的行列轨道布局 |
所以以下说法不准确:
display: flex或display: grid会创建一个新的传统 BFC。
更准确的说法是:
display: flex建立 Flex Formatting Context,display: grid建立 Grid Formatting Context。它们与 BFC 都属于格式化上下文,但采用不同规则。
justify-content 为什么不能代替 flex: 1
justify-content 负责分配主轴上的剩余空间,例如把 flex items 居中或在它们之间添加间隔:
css
.layout {
display: flex;
justify-content: space-between;
}
它不会自动规定"中间栏必须占满剩余宽度"。三栏方案仍需要通过 flex-grow、flex-basis 等属性定义各列伸缩关系。
同样,align-items 控制的是交叉轴对齐,也不负责三列宽度。
主内容优先要区分 DOM 顺序和视觉顺序
如果 DOM 顺序是:
text
左栏 → 主内容 → 右栏
那么主内容并不是源码中的第一个内容区域。若题目明确要求 main 优先出现在源码中,就需要重新设计 DOM 与视觉排列。
不过,不建议只为视觉效果随意改变 CSS order,因为 DOM 顺序还会影响:
- 键盘焦点顺序;
- 屏幕阅读器阅读顺序;
- 无样式情况下的内容顺序。
因此,"内容优先"不只是加载性能问题,还要兼顾可访问性与语义顺序。
面试回答模板
面试官问:"如何实现左右固定、中间自适应的三栏布局?"可以回答:
可以使用 Flex 或 Grid。Flex 方案中,容器设置
display: flex,左右栏设置flex: 0 0 200px,中间栏设置flex: 1,并常配合min-width: 0防止长内容撑破布局。Grid 方案可以直接设置grid-template-columns: 200px minmax(0, 1fr) 200px。三栏是一维问题,两者都适合;Flex 强调子项伸缩,Grid 对列尺寸的表达更直接。
面试官问:"Flex 和 Grid 会创建 BFC 吗?"可以回答:
它们不会让内部子元素按传统 BFC 规则布局。
display: flex和display: grid分别建立 Flex 和 Grid 格式化上下文。BFC、Flex、Grid 都是格式化上下文,但内部布局算法和适用场景不同。
最终自检清单
完成三栏布局后检查:
- 左右栏是否固定宽度且不会意外收缩?
- 中间栏是否真正吸收剩余空间?
- 长文本是否会撑破容器?
- 是否把
justify-content错当成列宽方案? - 是否把 Flex/Grid 错称为传统 BFC?
- 为创建 BFC 使用
overflow: hidden时,是否会误裁剪内容? - DOM 顺序是否符合阅读与键盘导航逻辑?
三栏布局的核心是明确尺寸关系;BFC 的核心是块级布局环境与隔离。把这两个问题分开,Flex、Grid 和 BFC 就不会再混淆。