跳转至

Flex Layout

Flex:CSS Flexible Box Layout,既:Flexbox(弹性盒布局)。顾名思义,就能了解两件事儿:

  1. Box 就有 Box 中存放的东西:

    • 我们管这个 Box 叫做 Flex ContainerFlex 容器);
    • 我们管放在 Box 中的“东西”叫做 Flex ItemFlex 项目),没有特别,只是放入了 FlexBox 中而已;
  2. 既然有 Flexbox(弹性盒布局),那么就有它的“兄弟”布局,这里先提前预报:

    • Block Layout(块级布局):不写 display 属性时,默认是 Block Layout;
    • Inline Layout(行内布局);
    • Grid Layout(网格布局):二维
    • Multi-column Layout(多列布局);
    • Box Alignment(盒子对齐)。

Inline LayoutBlock LayoutFlex LayoutGrid LayoutMulti-column Layout 都可以看作不同的布局模型(layout models),区别在于它们处理的对象和空间分配规则不同。

那么谁在“决定”一个 Box 是哪种类型的 Box 呢?

Box 元素的 display 属性并不是“外观属性”,而是“选择布局算法(layout algorithm)”。

所以:

  • display:flex = 启用 flex layout algorithm
  • display:block = 启用 block flow algorithm
  • display:grid = 启用 grid algorithm

注意:元素没变(还是那个 div 容器),变的是“怎么解释、计算它的位置和子元素”。

Flex Layout(Flex Box)到底是个什么?

它是一个“受约束的空间分配系统”,要了解它的一条链、两个约束、三个方向

  1. 一条控制链:

    Flex Box 不同于其他的容器,它不是自己设置自己就可以了,而是 祖先 --> 父亲 --> 自己(子) 这一连串的设置共同控制、设置、分配了一个空间。

  2. 两个核心约束:

    • 约束 1:父元素给了多少空间(height chain);
    • 约束 2:子元素允许缩多少(minHeight)。
  3. 三种“影响方向”:

    • 向下控制(应用于 Box 上):display:flex --> 控制 children 布局;
    • 自我控制(应用于 Box 中的 Item,这里是 Flex Item):flex:1 --> 控制自己如何占有空间;
    • 向上传递(非主动控制):Flex Item 占有空间的方式和内容多少 --> 撑大父元素(自然流)。

关于一条控制链需要说明的几个问题

为什么 height: 100% 经常“看似写了但没用”?

因为 height: 100% 的含义是“等于父元素的高度”。如果父元素高度是 auto(由内容撑开),那 100% of auto 还是 auto,不会给你一个确定高度。很多文章把这称为“高度链断了”,需要从 html/body/#root 一路检查。

Note

height: 100%flex: 1 想要计算成一个确定值,需要祖先节点逐层提供可计算的高度约束。

minHeight: 0minHeight: auto的区别

  • minHeight:min-width: 是 Flex Item 的非常关键的设置属性;
  • 关键规则:flex item 默认 min-height/min-widthauto,而不是 0;
  • flex item 的 min-width/min-height 默认是 auto,意味着“最小尺寸不能小于内容的最小内容尺寸(min-content size)”

min-width/min-height:auto(默认值),这会导致:

  • 你明明 flex: 1 想让它在有限空间内缩小;
  • 但它说“我最少得把内容装下”;
  • 于是它不肯缩,反而把父容器撑大,滚动也可能失效。

Note

flex: 1 失效、滚动不出现,很多时候是默认 min-height: auto 在阻止元素缩小

min-height: 0 到底改变了什么?

它做了一件很“硬核”的事:把最小高度从“内容决定”改成“允许缩到 0”。这样 flex-shrink 才能真正生效,子元素才会被压缩到父容器分配的高度内;如果内容超了,就交给 overflow(如 auto)在内部滚动,而不是把外层撑开。

height: 100% vs minHeight: 0:它们看起来都在控制 height,有什么关系?

这两个都“像是在管高度”,但它们控制的是完全不同的层面。

height: 100%:在做“目标高度”或“高度参照”。作用是告诉元素“你应该有多高”(相对父元素)。前提是父元素必须有可计算高度,否则无意义。

min-height: 0:在做“最小高度下限”。作用是告诉浏览器“你最少可以缩到多少”(覆盖默认 auto)。它解决的是 flex item “不肯缩小”导致撑破布局的问题。

简单总结

  • height: 100% 解决“你该有多高”;
  • min-height: 0 解决“你能不能被压到那么高”。
  • min-height: auto(默认)的实际含义是:这个元素最低不能小于内容高度(min-content height)。如果内容很高,元素就不肯缩小,可能导致父容器被撑大,滚动失效。
  • height: 100% 的作用是定义“目标高度”(你想变多高)。它依赖父高度约束,不能是 auto,否则 100% 无法计算,等于 auto。举例来说,height:100% 就是跟父元素一样高。

Important

height: 100% 必须依赖父高度约束,父高度不能是 auto,否则 100% 无法计算,等于 auto

  • flex: 1 本质是 flex-grow / flex-shrink / flex-basis 的简写,等价于 flex: 1 1 0%,即 flex-grow: 1; flex-shrink: 1; flex-basis: 0%;

容易出错的设置

display: flexflex: 1 是什么关系?有什么区别?

display: flex

  • 让父元素变成 Flex 容器;
  • 写在容器(parent上:display: flex(或 inline-flex);
  • 含义:开启 Flexbox 布局模型,容器的直接子元素成为 flex items,参与 Flex 排版与伸缩。

flex: 1

  • 让子元素按比例伸缩;
  • 写在flex itemchild上:flex: 1;
  • 它是三个属性的简写:flex-growflex-shrinkflex-basis,控制“如何分配剩余空间”和“空间不足如何收缩”。

flex: 1

  • “1”不是普通的长度数值,而是“比例权重”,但它自己不知道分母是多少;
  • flex:1 不等于“占满空间”;

很多人认为 flex:1 = 100%,这是错误的。它的真正含义是:

  • 在剩余空间里按比例分配,权重是 1,但自己不知道兄弟们的权重,自然也就不知道自己的这个 1 到底占多少权重;
  • 剩余空间的计算方式是:父容器的总空间减去已占用空间(由 flex-basis 或内容决定);
  • flex-grow 的值是“相对权重”,分母等于当前 flex 行内所有 item 的权重总和。

举个最直观的例子:

    .container {
        display: flex;
    }

    .A {
        flex: 1;
    }

    .B {
        flex: 2;
    }

如果剩余空间是 300px

  • 总权重 = 1 + 2 = 3
  • A → 100px
  • B → 200px

flex-grow 只分“剩余空间”

注意:它不是分配整个容器,而是分配剩余空间(free space)。

“剩余空间” = 容器分配给这一行的空间 − 所有子元素的“基础占用空间”。

先占空间的,是“基础尺寸(flex-basis / width / 内容)”这一层。

顺序如下。

CSS Flex 的本质不是“占空间”,而是:在约束条件下求解一个分配结果。空间分配优先级如下:

  1. 基础空间(Base Size)第一优先

    • flex-basis(基础尺寸,如果设置了)
    • 否则 width / height(如果设置了)
    • 否则内容尺寸(content size
  2. 瓜分剩余空间(Free Space)第二优先

    • 才进入:flex-grow 分配
  3. 如果空间不够(负剩余空间),进入另一套系统

    • flex-shrink 压缩

可以直接用的工程级规则

  • 要想子元素 height:100% 生效,先保证父元素高度“可计算”(高度链不断);
  • 在垂直 flex 布局里,想让内容区可滚动、不撑爆,关键层要写 min-height:0(解除默认 min-height:auto);
  • display:flex 是容器规则,flex:1 是子项伸缩规则,两者不是替代关系,是“舞台 vs 演员”。
  • display:flex 的作用是:“把当前节点变成一个 layout system(容器),管辖它的 children”。

如果不是 flex 容器:

  • flex:1 不起作用;
  • justify-content 无效。

CSS 属性分两类

  1. 类 1:独立属性(Universal

    • background
    • color
    • border
    • padding
    • margin

    这类属性不依赖 layout system,在任何 box 上都生效。

  2. 类 2:依赖 layout context 的属性。

    属于 flex container 的:

    • justify-content
    • align-items
    • flex-direction

    这类属性必须配合 display: flex; 才有意义。

    属于 flex item 的:

    • flex
    • align-self
    • order

    这类属性要求父元素是 flex container,否则会被忽略(不是报错,而是 silently ignore)。

Note

display 不是“外观属性”,而是“选择布局算法(layout algorithm)”。所以:

1
2
3
- `display:flex`:启用 flex layout algorithm;
- `display:block`:启用 `block flow algorithm`;
- `display:grid`:启用 `grid algorithm`。

注意:元素没变(还是那个 div),变的是“怎么解释、计算它的位置和子元素”。