CSS 三栏布局与 BFC 深度解析:Float、Flex、Grid 三种方案一网打尽
本文是一篇 CSS 学习日志。三栏布局是 PC 端最经典的布局需求,也是前端面试的高频题。本文先把「BFC」这个绕不开的概念讲透,再用 Float、Flex、Grid 三种方案逐一实现「左右固定、中间自适应」的三栏布局,并讲清一个关键细节------为什么
main内容区要在 DOM 里最先渲染。
目录
- 一、需求:什么是三栏布局
- [二、前置知识:BFC 到底是什么](#二、前置知识:BFC 到底是什么 "#%E4%BA%8C%E5%89%8D%E7%BD%AE%E7%9F%A5%E8%AF%86bfc-%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88")
- [三、方案一:Float 浮动布局(圣杯 / 双飞翼)](#三、方案一:Float 浮动布局(圣杯 / 双飞翼) "#%E4%B8%89%E6%96%B9%E6%A1%88%E4%B8%80float-%E6%B5%AE%E5%8A%A8%E5%B8%83%E5%B1%80%E5%9C%A3%E6%9D%AF--%E5%8F%8C%E9%A3%9E%E7%BF%BC")
- [四、方案二:Flex 弹性布局(推荐)](#四、方案二:Flex 弹性布局(推荐) "#%E5%9B%9B%E6%96%B9%E6%A1%88%E4%BA%8Cflex-%E5%BC%B9%E6%80%A7%E5%B8%83%E5%B1%80%E6%8E%A8%E8%8D%90")
- [五、方案三:Grid 网格布局(最优雅)](#五、方案三:Grid 网格布局(最优雅) "#%E4%BA%94%E6%96%B9%E6%A1%88%E4%B8%89grid-%E7%BD%91%E6%A0%BC%E5%B8%83%E5%B1%80%E6%9C%80%E4%BC%98%E9%9B%85")
- [六、关键细节:main 为什么要先渲染](#六、关键细节:main 为什么要先渲染 "#%E5%85%AD%E5%85%B3%E9%94%AE%E7%BB%86%E8%8A%82main-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%85%88%E6%B8%B2%E6%9F%93")
- 七、三种方案对比
- 八、总结
一、需求:什么是三栏布局
三栏布局是 PC 端网页最常见的骨架,比如经典的「论坛 / 后台 / 博客」首页:
scss
┌──────────────────────────────────────────────────────────────────┐
│ Left 侧边栏 │ Main 内容区(自适应) │ Right 侧边栏 │
│ (固定宽度) │ (随窗口伸缩) │ (固定宽度) │
└──────────────────────────────────────────────────────────────────┘
核心需求有三条:
- 左右两列固定宽度 ,中间列自适应(跟着浏览器窗口宽度伸缩);
main内容区最先加载------中间先渲染,让用户最快看到最有价值的内容;- 侧边栏可以晚一点加载------侧边栏通常是广告、导航栏等内容,优先级低于正文。
为什么「先渲染 main」这么重要?网页从上到下解析 DOM,谁在 DOM 里靠前,谁就先被渲染。把最重要的正文放最前面,用户就能最快看到核心内容,广告导航这些次要信息往后排。这也是 SEO 和首屏性能优化的常识。
下面先解决一个所有方案都绕不开的概念------BFC。
二、前置知识:BFC 到底是什么
2.1 从「文档流」说起
HTML 从 <html> 根元素开始,就开启了一个最基本的布局规则------文档流:
- 块级元素(block):从上到下垂直排列;
- 行内元素(inline):从左到右水平排列。
这是浏览器默认的排版方式,也是最基础的那个「格式化上下文」。
2.2 什么是格式化上下文(Formatting Context)
格式化上下文(Formatting Context) 是一块独立的渲染区域,它规定了这块区域内部的子元素如何排列、如何和外部交互。
常见的有:
| 缩写 | 全称 | 含义 |
|---|---|---|
| BFC | Block Formatting Context | 块级格式化上下文 |
| FFC | Flex Formatting Context | 弹性格式化上下文 |
| GFC | Grid Formatting Context | 网格格式化上下文 |
| IFC | Inline Formatting Context | 行内格式化上下文 |
2.3 什么是 BFC
BFC(Block Formatting Context,块级格式化上下文) 是页面里一块独立的布局区域 ,内部有自己的排版规则,且与外部相互隔离、互不干扰。
一句话记住它的两个关键特性:
外层的 BFC 不会影响到内层的 BFC;内层的 BFC 也基本不影响外层(除非内层自身触发了某些特殊规则,比如 margin 塌陷、浮动高度塌陷)。
2.4 如何开启一个新的 BFC
一个块级元素,满足下面任意一个条件,就会开启一个新的 BFC:
css
/* 1. 根元素 <html> 本身就是第一个 BFC */
/* 2. display 设为这些值 */
display: flex; /* 弹性容器,开启的是 FFC(弹性格式化上下文) */
display: grid; /* 网格容器,开启的是 GFC(网格格式化上下文) */
display: table; /* 表格 */
display: flow-root; /* 专门用来开启 BFC 的属性 */
/* 3. 浮动 / 定位 */
float: left; /* float 不为 none */
position: absolute; /* position 为 absolute 或 fixed */
/* 4. overflow 不为 visible */
overflow: hidden;
注意区分:
display: flex其实开启的是 FFC (弹性格式化上下文),display: grid开启的是 GFC(网格格式化上下文),它们都是「格式化上下文」,只是规则不同。这里统一理解成「开启了一个新的、独立的布局区域」即可。
2.5 BFC 能解决什么问题(面试重点)
- 外边距折叠(margin 塌陷):父子元素或兄弟元素之间的 margin 重叠;
- 高度塌陷 :父元素里的子元素都
float了,父元素高度变成 0(清除浮动的原理); - 文字环绕:让文字不再环绕浮动元素(两栏自适应布局的原理)。
三栏布局里,float 方案能自适应,本质就是利用了 BFC 的「不与浮动元素重叠」这一特性。
三、方案一:Float 浮动布局(圣杯 / 双飞翼)
Float 布局是最经典、历史最悠久的方案,面试也最爱考。
3.1 核心思路
- 三栏都
float: left,中间列宽度100%; - 用负 margin 把左右两栏「拽」到同一行;
- 中间列通过
padding(圣杯)或内层容器margin(双飞翼)给左右侧栏腾出位置。
3.2 代码实现(圣杯布局)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>三栏布局 - Float(圣杯布局)</title>
<style>
* { margin: 0; padding: 0; }
.container { padding: 0 200px 0 150px; } /* 左右腾出侧栏位置 */
/* 三栏都浮动 */
.col { float: left; min-height: 400px; }
.main {
width: 100%; /* 中间自适应 */
background: #f0f0f0;
}
.left {
width: 150px;
margin-left: -100%; /* 拽到最左边 */
position: relative;
left: -150px; /* 回到腾出的左 padding 位置 */
background: #ffe0e0;
}
.right {
width: 200px;
margin-left: -200px; /* 用自身宽度拽回上一行 */
position: relative;
right: -200px; /* 回到腾出的右 padding 位置 */
background: #e0e0ff;
}
</style>
</head>
<body>
<div class="container">
<main class="col main">Main Content(自适应)</main>
<aside class="col left">Left 150px</aside>
<aside class="col right">Right 200px</aside>
</div>
</body>
</html>
3.3 双飞翼布局(另一种思路)
区别只在:不用外层 padding,而是给 main 里再套一层 margin 来腾位置 ,从而避免圣杯布局里用 position: relative 移动的「脆弱感」。
html
<style>
.container { min-width: 350px; }
.col { float: left; }
.main { width: 100%; background: #f0f0f0; }
.main-inner { margin: 0 200px 0 150px; min-height: 400px; } /* 内层腾位置 */
.left { width: 150px; margin-left: -100%; background: #ffe0e0; }
.right { width: 200px; margin-left: -200px; background: #e0e0ff; }
</style>
<div class="container">
<main class="col main">
<div class="main-inner">Main Content(自适应)</div>
</main>
<aside class="col left">Left 150px</aside>
<aside class="col right">Right 200px</aside>
</div>
3.4 优缺点
- ✅ 兼容性极好(连 IE 都支持),「main 先渲染」天然满足;
- ❌ 代码复杂、语义不清晰,负 margin 和
position很难维护; - ❌ 需要额外处理清除浮动(父容器高度塌陷)。
结论:面试会问原理,生产项目基本不用,看懂即可。
四、方案二:Flex 弹性布局(推荐)
Flex 是现在实际开发中最常用的方案,代码简单、语义清晰。
4.1 核心思路
- 容器
display: flex,开启弹性格式化上下文(FFC); - 左右两栏写死宽度(
flex: 0 0 150px表示「不放大、不缩小、固定 150px」); - 中间
flex: 1,占据剩余所有空间。
4.2 代码实现
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>三栏布局 - Flex</title>
<style>
* { margin: 0; padding: 0; }
.layout {
display: flex; /* 开启弹性格式化上下文 */
}
.sidebar {
/* flex: 0 0 宽度 = 不放大 不缩小 固定宽度 */
flex: 0 0 150px;
min-height: 400px;
}
.left { background: #ffe0e0; }
.right { background: #e0e0ff; flex-basis: 200px; }
.content {
flex: 1; /* 占据剩余空间,自适应 */
background: #f0f0f0;
min-height: 400px;
}
</style>
</head>
<body>
<div class="layout">
<aside class="sidebar left">Left 150px</aside>
<main class="content">Main Content(自适应)</main>
<aside class="sidebar right">Right 200px</aside>
</div>
</body>
</html>
4.3 flex 三连击拆解
flex: 0 0 150px 是三个属性的简写:
css
flex: 0 0 150px;
/* = flex-grow: 0; ------ 不放大,不参与剩余空间分配
flex-shrink: 0; ------ 不缩小,空间不足也不压缩
flex-basis: 150px; ------ 初始宽度 150px */
中间列 flex: 1 等价于 flex: 1 1 0%:
css
flex: 1;
/* = flex-grow: 1; ------ 放大,吃掉所有剩余空间
flex-shrink: 1; ------ 允许缩小
flex-basis: 0%; ------ 基准宽度为 0,完全靠 grow 分配 */
4.4 优缺点
- ✅ 代码简洁、语义清晰,最好维护;
- ✅ 垂直居中、等高列这些 flex 都顺手解决;
- ✅ 兼容性现代浏览器全覆盖;
- ⚠️ 默认 DOM 顺序 = 视觉顺序,要让
main先渲染需要用order调整(见第六节)。
五、方案三:Grid 网格布局(最优雅)
Grid 是专门做二维布局的,三栏布局对它来说就是一行三列的小儿科。
5.1 核心思路
- 容器
display: grid,开启网格格式化上下文(GFC); - 用
grid-template-columns直接定义三列宽度。
5.2 代码实现
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>三栏布局 - Grid</title>
<style>
* { margin: 0; padding: 0; }
.layout {
display: grid;
/* 三列:左 150px | 中间自适应 1fr | 右 200px */
grid-template-columns: 150px 1fr 200px;
min-height: 400px;
}
.left { background: #ffe0e0; }
.content { background: #f0f0f0; }
.right { background: #e0e0ff; }
</style>
</head>
<body>
<div class="layout">
<aside class="left">Left 150px</aside>
<main class="content">Main Content(自适应)</main>
<aside class="right">Right 200px</aside>
</div>
</body>
</html>
5.3 fr 单位
1fr 里的 fr 是 fraction(份数) 的意思:
css
grid-template-columns: 150px 1fr 200px;
/* 150px 和 200px 固定占用,剩下的所有空间全部分给 1fr 这一列 */
这就是 Grid 的自适应:固定列写死,自适应列用 fr。
5.4 优缺点
- ✅ 语义最清晰、代码最短,专为二维布局设计;
- ✅ 复杂布局(不等高、跨行列)能力最强;
- ⚠️ 兼容性略逊于 flex(IE11 不支持,现代项目无压力)。
六、关键细节:main 为什么要先渲染
前面反复强调「main 内容区要先渲染」,这是三栏布局的隐藏考点。
6.1 原理
浏览器从上到下解析 HTML,DOM 里靠前的元素先被渲染。所以:
- 把
main(正文)放在 DOM 的最前面 → 用户最先看到正文; aside(广告、导航)放后面 → 次要内容晚点出现,不影响首屏。
6.2 Float / Grid 天然满足,Flex 需要 order
- Float(圣杯 / 双飞翼) :代码里
main就写在最前面,天然满足 ✅; - Grid :可以用
grid-template-areas显式指定区域,配合order也能满足 ✅; - Flex :DOM 顺序 = 视觉顺序,默认
main在中间。要让它「视觉在中间、DOM 在最前」,需要order:
html
<style>
.layout { display: flex; }
.content { flex: 1; order: 2; } /* 视觉排第 2,居中 */
.left { flex: 0 0 150px; order: 1; }
.right { flex: 0 0 200px; order: 3; }
</style>
<div class="layout">
<main class="content">Main(DOM 最前,先渲染)</main>
<aside class="left">Left</aside>
<aside class="right">Right</aside>
</div>
order默认是0,值越小越靠前;- 这里给三栏分别标
1 / 2 / 3,让视觉顺序变成 Left → Main → Right ,但 DOM 里 Main 仍然排第一,既保证先渲染,又保证视觉居中。
面试追问「怎么保证中间列先加载?」------答案就是:DOM 顺序把 main 放最前,再用
order或负 margin 调整视觉顺序。
7. 三种方案对比
| 维度 | Float(圣杯/双飞翼) | Flex | Grid |
|---|---|---|---|
| 代码复杂度 | 高(负 margin、定位) | 低 | 最低 |
| 可维护性 | 差 | 好 | 最好 |
| main 先渲染 | 天然满足 | 需 order |
需 order/areas |
| 兼容性 | 最好(含 IE) | 现代浏览器 | 现代浏览器(不含 IE) |
| 适用场景 | 面试 / 老项目 | 日常首选 | 复杂二维布局 |
结论 :日常开发优先用 Flex (简单场景)或 Grid(复杂二维场景);Float 方案主要用于理解原理和应对面试。
八、总结
这篇文章从「三栏布局」这个最经典的需求出发,串起了 CSS 布局的核心知识:
- 需求本质 :左右固定、中间自适应,且
main要先渲染; - BFC 概念 :格式化上下文是「独立的布局区域」,
display: flex开 FFC、display: grid开 GFC、float/overflow: hidden等开新的 BFC; - 三种方案 :Float(负 margin,历史经典)、Flex(
flex: 1+order,日常首选)、Grid(1fr,最优雅); - 关键细节 :
main先渲染靠 DOM 顺序 +order/负 margin 调整视觉顺序。
一句话总结 :三栏布局 = 左右定宽 + 中间
flex:1(或1fr),再配上「main放 DOM 最前」的渲染优化。
·