模块三 · 样式与类型
CSS 布局与层叠:把页面摆对
布局问题大多不是"属性没记住",而是盒模型、包含块、BFC 与层叠上下文没理清。这一节按这四条主线把 CSS 讲成可推理的规则。
读完这一节,你能做到
- 用盒模型与包含块解释一个元素的最终尺寸与位置。
- 说清 BFC 解决什么问题、怎么触发。
- 判断 z-index 为什么"不生效"。
- 在 Flex 与 Grid 之间做出有依据的选择。
一、盒模型与包含块
每个元素都是一个盒子:content + padding + border + margin。
box-sizing: content-box; /* 默认:width 只算 content */
box-sizing: border-box; /* width 包含 padding 与 border */
工程上通常全局设 border-box,让"宽度"符合直觉。
包含块决定百分比宽度、绝对定位的参照:
| position | 包含块 |
|---|---|
| static/relative | 最近的块级祖先的内容盒 |
| absolute | 最近的定位祖先的 padding 盒 |
| fixed | 视口 |
| sticky | 最近的滚动容器 |
很多"绝对定位跑偏"的问题,都是因为没找到真正的包含块。
二、BFC:块级格式化上下文
BFC 是一个独立的渲染区域,内部的布局不影响外部。它能解决:
- 浮动元素导致父容器高度塌陷。
- 相邻元素 margin 折叠。
- 浮动元素环绕文字。
触发方式(任选其一):overflow 非 visible、display: flow-root、position: absolute/fixed、float 非 none、contain: layout。
推荐 display: flow-root:语义清晰、没有副作用,不像 overflow: hidden 会裁剪内容。
三、层叠上下文与 z-index
z-index 只在同一层叠上下文内比较。以下情况会创建新的层叠上下文:
- 根元素。
position非 static 且 z-index 非 auto。opacity < 1、transform、filter、will-change。isolation: isolate。
所以一个父元素设了 transform,子元素的 z-index: 9999 也可能压不过外部的元素——它只在父级上下文内有效。
调试方法:从目标元素往上找,看哪一层创建了新的上下文。
四、Flex 与 Grid 怎么选
| 维度 | Flex | Grid |
|---|---|---|
| 方向 | 一维(行或列) | 二维(行与列) |
| 适合 | 导航、工具栏、卡片内部 | 页面骨架、卡片网格 |
| 对齐 | 主轴 + 交叉轴 | 行轴 + 列轴 |
| 自适应 | flex: 1 分配剩余空间 | minmax + auto-fit |
一个实用判断:只需要在一个方向上分配空间,用 Flex;需要同时控制行和列,用 Grid。
五、响应式:三种手段
- 媒体查询:按视口宽度切换布局,适合页面级断点。
- 容器查询:按父容器宽度切换,适合组件级自适应。
- 流式单位:
clamp()、min()、max()、fr,让尺寸连续变化。
.card {
width: clamp(240px, 30vw, 360px);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
优先用流式布局,减少断点数量;断点只处理"结构变化",不要为每个尺寸写一套。
六、常见布局问题
- 水平垂直居中:Flex 的
justify-content + align-items最稳;绝对定位 + transform 适合脱离文档流。 - 两栏等高:Flex 默认 stretch;Grid 也天然等高。
- 圣杯布局:Grid 的
grid-template-areas最直观。 - 文字溢出:单行
text-overflow: ellipsis+white-space: nowrap;多行用-webkit-line-clamp。 - 1px 边框:高倍屏用
transform: scaleY(0.5)或直接接受。
七、常见坑
- 给所有元素设
border-box后,仍按 content 宽度算。 - 用
overflow: hidden清浮动,裁掉了下拉菜单。 - z-index 不生效,实际是父级创建了新层叠上下文。
- Flex 子项不换行,忘了
flex-wrap。 - Grid 的
1fr被内容撑破,需要minmax(0, 1fr)。
八、动手任务:自写 → AI 写 → 对比 → 取舍
四步不跳:① 自己写(限时,关掉补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁的写法。 完整动作与记录模板见《AI 协作与代码评审》。
① 自己写
- 用
flow-root修一次高度塌陷,对比overflow: hidden的副作用。 - 写一个"父级 transform 导致 z-index 失效"的例子,并修复。
- 用 Grid 的
auto-fit + minmax实现自适应卡片网格。
② 让 AI 写同一题
把第 2 题(页面布局)交给 AI,补一句 不要用绝对定位硬摆,说明为什么选 flex 或 grid。
③ 对比要盯的点
- 它选 flex 还是 grid?理由是内容驱动还是布局驱动?
- 外边距折叠了解吗?父子、兄弟折叠的规则与 BFC 的修法。
- 移动端
100vh它有没有处理?浏览器地址栏会导致溢出。
④ 一句话取舍
它这套布局在 320px 宽的老手机上会怎样?
九、自测
- content-box 与 border-box 的差异?
- BFC 能解决哪三类问题?
- 哪些属性会创建层叠上下文?
- Flex 与 Grid 的选择标准?
- 容器查询与媒体查询的区别?
参考答案要点:宽度是否含内边距;塌陷/折叠/环绕;定位+z-index、opacity、transform;一维 vs 二维;父容器 vs 视口。
附录:高频追问
问:margin 折叠发生在什么情况? 相邻兄弟、父子(无内边距/边框/内容分隔)、空元素自身;BFC 可以阻止父子折叠。
问:position: sticky 不生效? 常见原因是祖先有 overflow: hidden/auto,或没有设置 top 等阈值。
问:1fr 为什么会被撑破? 1fr 的最小值是 auto,内容过宽会撑开;用 minmax(0, 1fr) 限制。
问:z-index 负值有用吗? 有,但在同一层叠上下文内,可能被父级背景遮挡,慎用。
问:Flex 的 flex: 1 等价于什么? flex-grow: 1; flex-shrink: 1; flex-basis: 0%,注意 basis 是 0 而不是 auto。
问:Grid 能替代 Flex 吗? 不能。一维布局 Flex 更简单,二维布局 Grid 更直接,实际项目常混用。
问:如何做响应式字体? 用 clamp() 设置最小、首选、最大值,避免媒体查询堆叠。
问:will-change: transform 有什么风险? 长期占用合成层与显存,动画结束应移除。
布局的计算成本
布局是从上到下的几何计算,成本与节点数量、嵌套深度、浮动/定位的复杂度相关。频繁触发布局的写法,在低端设备上会被放大。
降低布局成本的手段:减少 DOM 数量、避免深层嵌套、用 contain: layout 隔离子树、把动画放到 transform。
三栏布局的演进
- 浮动 + 负 margin:兼容老浏览器,但难以维护。
- Flex:中间自适应,两侧固定,代码短。
- Grid:
grid-template-columns: 200px 1fr 200px,语义最清晰。
面试时讲清“为什么从浮动演进到 Grid”,比背三套代码更能体现理解。
居中的四种写法
| 方式 | 写法 | 适用 |
|---|---|---|
| Flex | justify-content + align-items | 未知尺寸,最稳 |
| Grid | place-items: center | 父级为 grid 时最简 |
| 绝对定位 + transform | left:50%; top:50%; transform:translate(-50%,-50%) | 脱离文档流 |
| margin auto | 固定宽高 + margin:auto | 块级元素 |
能用 Flex/Grid 就不用绝对定位,后者需要知道尺寸且影响文档流。
移动端 1px 与安全区
1px 边框在高倍屏会显示成 2-3 物理像素,常见解法是用伪元素 + transform: scaleY(0.5)。刘海屏用 env(safe-area-inset-*) 给底部与顶部留出安全区,避免被遮挡。
移动端还要注意 300ms 点击延迟、滚动穿透与输入框被键盘顶起,这些都不是“CSS 写得好”能解决的,需要配合 JS 与视口设置。
逻辑属性与书写方向
国际化项目用逻辑属性替代方向属性:margin-inline、padding-block、inset-inline-start。它们在 LTR 与 RTL 下自动镜像,不用写两套样式。
判断标准:如果页面要支持阿拉伯语或希伯来语,一开始就用逻辑属性,后期改造成本很高。
选择器性能
浏览器匹配选择器从右往左,过深的后代选择器与通配符会增加匹配成本。但样式匹配通常不是瓶颈,布局与绘制才是;先修布局,再谈选择器。先布局,后选择器;结构决定成本,少一层嵌套,少一分成本。先结构,后样式。先结构,再样式。
配套题目
★ 越多越该先背;点题目可看完整答案与深入版。
- ★★★★☆ 说一下 BFC,它能解决什么问题?
- ★★★★☆ 元素垂直水平居中有哪几种写法?各自限制是什么?
- ★★★★☆ Flex 和 Grid 怎么选?各自适合什么布局?
- ★★★★☆ 浏览器的本地存储方式有哪些?怎么选?
提示:读完可以顺手做几道对应题目,检验是否真的理解