写 CSS 久了会发现,很多"莫名其妙"的间距问题,最后都能追溯到两件事:盒模型到底怎么算宽度,以及外边距为什么会合并。这两个概念搞清楚,布局里的坑能少一半。
两种盒模型
浏览器把每个元素都看成一个矩形盒子,从内到外依次是 content、padding、border、margin。区别在于:width 这个属性,到底描述了哪一部分的宽度。
- 标准盒模型(
content-box):width只算内容区。实际占位还要加上 padding 和 border。 - IE 盒模型(
border-box):width包含了 content、padding 和 border 三部分。
举个最直观的例子:
.box {
width: 200px;
padding: 20px;
border: 5px solid #ddd;
}
/* content-box:实际宽度 = 200 + 20*2 + 5*2 = 250px */
/* border-box:实际宽度 = 200px,内容区被压缩到 150px */
为什么大家都用 border-box
因为布局时我们关心的是"这个块在页面上占多宽",而不是"内容区有多宽"。用 content-box 时,一旦给元素加了 padding,就得重新心算总宽度,栅格布局里尤其容易算错。所以现在项目初始化基本都会全局重置:
*,
*::before,
*::after {
box-sizing: border-box;
}
这行代码的代价很小,收益是后续所有尺寸计算都变得可预测。唯一要注意的是,第三方组件的样式如果依赖 content-box,可能会被影响,接入前扫一眼即可。
外边距折叠是什么
在普通流中,两个垂直相邻的 margin 不会相加,而是取其中较大的那个,这就是外边距折叠(margin collapsing)。
常见三种情况:
- 相邻兄弟元素:上面元素的
margin-bottom与下面元素的margin-top合并。 - 父子元素:父元素没有 border、padding 或 BFC 时,子元素的
margin-top会"穿透"到父元素外面。 - 空块级元素:自身的上下 margin 会折叠。
需要说明的是,只有垂直方向会折叠,水平方向的 margin 永远相加。另外 flex 和 grid 容器内部不会发生折叠,这也是很多人觉得"用了 flex 后 margin 突然正常了"的原因。
怎么规避
- 父子折叠:给父元素加
overflow: hidden、padding或border,触发 BFC。 - 兄弟折叠:只在一个方向上设 margin,比如统一用
margin-bottom,是更省心的约定。 - 统一交给 flex/grid 的
gap处理间距,从根上绕开这个问题。
小结
盒模型决定"元素有多大",外边距折叠决定"元素之间隔多远"。前者用 border-box 一劳永逸,后者用 gap 或单向 margin 约定就能避开。真遇到算不清的间距,先在浏览器 DevTools 里看一眼盒模型图,通常比猜要快得多。
— 全文完 —