Skip to content

BFC (Block Formatting Context) ​

BFC 是关于 CSS 布局的经典面试题。理解 BFC 才能正确处理边距折叠、清除浮动等问题。

什么是 BFC? ​

BFC = 块级格式化上下文,是页面中一块独立的渲染区域。

可以把它理解为 CSS 世界的"结界":内部的布局不会影响外部。

正常文档流:
  ┌──────────┐      ┌──────────┐
  │  Box 1   │  ←→  │  Box 2   │   两个盒子共享同一个 BFC
  └──────────┘      └──────────┘   会发生 margin 重叠

BFC 隔离:
  ┌──────────────┐
  │  BFC 容器     │
  │  ┌──────────┐ │     ┌──────────┐
  │  │  Box 1   │ │ ←→  │  Box 2   │   margin 不重叠
  │  └──────────┘ │     └──────────┘
  └──────────────┘

如何创建 BFC? ​

css
/* 以下任一条件都可触发 BFC: */

/* ① overflow 不为 visible */
.bfc { overflow: hidden; }
.bfc { overflow: auto; }
.bfc { overflow: scroll; }

/* ② float 不为 none */
.bfc { float: left; }
.bfc { float: right; }

/* ③ position 为 absolute 或 fixed */
.bfc { position: absolute; }

/* ④ display 为以下值 */
.bfc { display: inline-block; }
.bfc { display: flow-root; }  /* 专门创建 BFC,无副作用 */
.bfc { display: table-cell; }
.bfc { display: flex; }
.bfc { display: grid; }

/* ⑤ contain 属性 */
.bfc { contain: layout; }
.bfc { contain: paint; }
/* ⭐ 现代最佳实践:display: flow-root */

BFC 的特性 ​

① 内部垂直 margin 不会溢出 ​

html
<style>
  .parent {
    background: lightblue;
    /* 没有 BFC:子元素 margin-top 30px 会溢出到父元素上 */
  }
  .parent.bfc {
    overflow: hidden;  /* 创建 BFC */
    /* 有 BFC:子元素 margin-top 被包含在父元素内 */
  }
  .child {
    margin-top: 30px;
  }
</style>
<div class="parent">
  <div class="child">content</div>
</div>

② 解决 margin 重叠 ​

html
<style>
  .box { margin: 20px; background: lightcoral; }
</style>

<!-- margin 重叠:两个 20px 间隙不是 40px 而是 20px -->
<div class="box">box1</div>
<div class="box">box2</div>

<!-- 放入 BFC 容器解决 -->
<div class="box">box1</div>
<div style="overflow: hidden;">
  <div class="box">box2</div>  <!-- margin 不重叠了 → 40px -->
</div>

③ 清除浮动(包含浮动子元素) ​

html
<style>
  .float { float: left; width: 100px; height: 100px; }
  /* 父元素没有高度!浮动元素脱离文档流 */
</style>

<div class="parent">
  <div class="float"></div>
</div>

<!-- 解决方案一:overflow: hidden 创建 BFC -->
<div class="parent" style="overflow: hidden;">
  <div class="float"></div>
</div>

<!-- 解决方案二:display: flow-root(现代方式,无副作用) -->
<div class="parent" style="display: flow-root;">
  <div class="float"></div>
</div>

④ BFC 不会与浮动元素重叠 ​

html
<!-- 经典两栏布局 -->
<style>
  .aside {
    float: left;
    width: 200px;
    background: lightblue;
  }
  .main {
    overflow: hidden;  /* 创建 BFC */
    /* main 的左边会紧贴 aside,而不是重叠在下面 */
  }
</style>
<div class="aside">侧边栏</div>
<div class="main">主区域</div>

BFC 布局规则总结 ​

1. 内部 Box 垂直排列
2. Box 间垂直距离由 margin 决定
3. 同一 BFC 内的 Box 的垂直 margin 会重叠
4. BFC 区域不与 float box 重叠
5. BFC 是一个独立容器,内部不影响外部
6. 计算 BFC 高度时,浮动元素也参与计算(清除浮动)

在 Vue 中的应用 ​

Transition 组件与 BFC ​

html
<!-- Vue Transition 的 leave-active 阶段 -->
<Transition name="fade">
  <div v-if="show" class="content">
    <!-- leave 动画执行时,内容已从 DOM 移除 -->
    <!-- 但 Vue 会保留元素直到动画结束 -->
    <!-- 这期间下面的元素可能跳动 -->
  </div>
</Transition>

<!-- 解决方案:给 Transition 包一层 BFC -->
<div style="overflow: hidden;">
  <Transition name="fade">
    <div v-if="show">content</div>
  </Transition>
</div>

列表动画的布局浮动 ​

html
<!-- Vue 的 TransitionGroup 移动动画 -->
<TransitionGroup name="list" tag="div">
  <div v-for="item in items" :key="item.id">
    {{ item.name }}
  </div>
</TransitionGroup>

<style>
.list-move {
  transition: transform 0.5s;
}
/* TransitionGroup 用 FLIP 动画,
   基于 transform 完成,不触发布局
   比修改 margin 之类要好得多 */
/* 但要注意内部不能有 float 元素 → BFC 可以隔离 */
</style>

经典面试题 ​

题1:margin 重叠 ​

html
<div style="margin-bottom: 30px;">A</div>
<div style="margin-top: 50px;">B</div>
<!-- 间距 = max(30, 50) = 50px,不是 80px -->

<!-- 解决 -->
<div style="margin-bottom: 30px;">A</div>
<div style="display: flow-root;">
  <div style="margin-top: 50px;">B</div>  <!-- 现在间距 = 80px -->
</div>

题2:清除浮动的多种方式 ​

css
/* ① BFC */
.parent { overflow: hidden; }

/* ② clearfix(经典伪元素方案) */
.parent::after {
  content: '';
  display: block;
  clear: both;
}

/* ③ display: flow-root(现代,推荐) */
.parent { display: flow-root; }

/* ④ 父元素也浮动(不推荐) */
.parent { float: left; }

基于 Vue 2.7 & Vue 3.4 源码