Skip to content

很多业务页本质都是同一类问题:固定高度的卡片里,上下几块内容怎么分高度——顶部信息、标题要完整展示,中间摘要尽量多吃剩余空间,底部审核区与主按钮永远留在视区内,中间过长则自己滚动、不把下面挤没。

前端布局有点像一套小型物理规则:不是「会背属性就自然会排」,默认行为里常有反直觉处——例如 flex-shrink 默认是 1,子项在主轴空间不够时会先被允许压扁,于是审核按钮区可能被挤没。下面从默认值说到审核页常用写法。


核心模型

css
.parent {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.auto {
  flex: 0 0 auto; /* 与「只写 flex-shrink: 0」在本场景等价,见下节 */
}

.fill {
  flex: 1 1 0;
  min-height: 0;
}

Flex 子项默认是 flex: 0 1 auto(为什么要单独管 flex-shrink

父级写了 display: flex 后,子项自己不写 flex 时,浏览器默认相当于:

css
.child {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
}

也就是 flex: 0 1 auto

  • flex-grow: 0:有剩余空间时,默认不主动变大。
  • flex-shrink: 1:空间不够时,默认允许被压缩——纵向卡片里空间吃紧时,浏览器就可能去压「审核区」「底栏」,标签和按钮被挤扁或看不全,常来自这里。
  • flex-basis: auto:先按内容尺寸或你写的 width / height 参与基础占位。

容易记混的一点:子项的 flex-grow 默认确实是 0——所以默认情况下子项不会主动抢剩余空间;但日常里很多人写的 flex: 1 会把 flex-grow 设成 1,和「什么都不写」不是一回事。

若你主动写:

css
.child {
  flex: 1;
}

在常见浏览器里会按 flex 简写规则解析为 flex-grow: 1flex-shrink: 1flex-basis: 0%,即:

css
.child {
  flex: 1 1 0%;
}

(规范里 00%flex-basis 上多数场景表现一致;写 flex: 1 1 0flex: 1 在「吃剩余空间」这件事上通常同一类意图。)

对照记:

css
/* 默认值:不抢剩余空间;空间不够时可以缩 */
.item {
  flex: 0 1 auto;
}

/* 主动写 flex: 1 后:抢剩余空间;也可以缩 */
.item {
  flex: 1 1 0%;
}

若你只写:

css
.child {
  flex-shrink: 0;
}

等价于把第二项改成 0,整体变成 flex: 0 0 auto不主动吃剩余空间、不允许被 flex 压扁、尺寸跟内容(或你设的宽高)走。

三句常用口诀可以背下来:

css
/* 固定 / 内容撑开:不许压扁 */
.fixed {
  flex: 0 0 auto;
}

/* 吃剩余空间;内容多了在本区滚 */
.fill {
  flex: 1 1 0;
  min-height: 0;
}

/* 就是浏览器默认:可以被别人挤一挤 */
.normal {
  flex: 0 1 auto;
}

扩展口诀(一条一行):

  • 不写 flex:默认 0 1 auto——不抢剩余空间,空间不够时可以被压缩。
  • flex: 1:常见等价 1 1 0%——主动吃剩余空间,也可以缩。
  • flex-shrink: 0:不许 flex 在主轴上压扁我(常与其它 longhand 组合成 0 0 auto 语义)。
  • flex: 0 0 auto:按内容(或你设的宽高)来,不抢也不缩
  • flex: 1 1 0 + min-height: 0(纵向可滚区):我吃剩余空间,内容多了只在自家滚动。

审核页卡片里:顶部、标题、审核操作、底栏都属于 「必须完整显示」 → 用 .fixed 语义(写 flex: 0 0 auto 最一眼可读;只写 flex-shrink: 0 也行,因为默认 grow 已是 0basis 仍是 auto,与本页需求一致)。


计算过程(直觉版)

假设父容器总高度 1000px,子元素从上到下排:

  1. 先算「由内容撑开」的自然高度
    例如:顶部信息 80px、标题区 80px、审核操作区 260px、底部按钮栏 60px——这些都当作「固定占用」,不参与抢剩余高度。

  2. 扣掉自然高度
    剩余 ≈ 1000 - 80 - 80 - 260 - 60 = 520px(数字仅作示意)。

  3. 把剩余高度给弹性区
    中间摘要区域写了 flex: 1 1 0(再配合 min-height: 0),就吃这 520px。

  4. 摘要超出时
    摘要区内部滚动,不会把审核区、按钮顶出卡片可视区域。

因此:审核按钮、标签始终可见;摘要尽量多展示,但不挤压操作区。


flex: 1 1 0 在干什么(以及和单写 flex: 1 的关系)

上文已说明:单写 flex: 1 常见即 flex: 1 1 0%,与默认 0 1 auto 的差别主要在 growbasis。中间摘要要占满「扣完固定区之后的剩余高度」,必须显式让 flex-grow1(写 flex: 1flex: 1 1 0 均可落在这一类意图上)。

用长属性写清楚就是:

css
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
  • flex-grow: 1:有剩余空间时,由我来吃。
  • flex-shrink: 1:空间不够时,我可以缩(配合内部滚动,而不是去顶兄弟节点)。
  • flex-basis: 0:参与分配剩余空间时,不要先按我的内容高度占位,从 0 开始分「多出来的那一截」。

中间「摘要 / 正文」这类区域适合:

css
.review-card__summary-scroll {
  flex: 1 1 0;
}

即:我是弹性区,剩多少给多少。


min-height: 0 为什么几乎必写

这是很多人踩坑的点。

Flex 子项默认等价于带有 min-height: auto 的语义:最小高度不能小于内容所需高度。摘要很长时,子项会说「我内容要 1000px,我最少也要 1000px」,结果把整个列撑爆,把下面的操作区挤出可视区域

所以要写:

css
.review-card__summary-scroll {
  min-height: 0;
}

含义:允许我比内容更矮;超出部分交给 overflow: auto / scroll-view本区域内部滚动。

没有 min-height: 0,单独写 flex: 1 经常「看起来不生效」。更一般的「横轴挤压文字」场景见同目录文:为什么 min-w-0 / min-h-0 能救活被 Flex 卡住的子元素?


套到你的页面结构(示例)

结构可以接近(小程序/uni-app 常用 view / scroll-view;Web 可把 view 换成 divscroll-view 换成带固定高度链路的容器 + overflow: auto):

html
<view class="review-card">
  <view class="review-card__top">顶部信息</view>
  <text class="review-card__title">标题</text>

  <scroll-view class="review-card__summary-scroll" scroll-y>
    摘要内容
  </scroll-view>

  <view class="review-mark">审核操作区</view>
  <view class="action-bar">确认按钮</view>
</view>

对应样式:

css
.review-card {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.review-card__top,
.review-card__title,
.review-mark,
.action-bar {
  flex: 0 0 auto;
}

.review-card__summary-scroll {
  flex: 1 1 0;
  min-height: 0;
}

效果归纳:

  • 顶部、标题:随内容高度。
  • 审核区:随标签、表单项自然撑开,且不被压扁
  • 底栏按钮:固定留在卡片底部可见
  • 剩余高度:全部给摘要
  • 摘要过长:只在摘要区域滚动

同一套思路也常见于移动端审核台、富文本编辑器、IM 聊天窗口、后台详情抽屉等。


建模时先问自己两句

  1. 「谁必须完整显示?」 → 这些块用 flex: 0 0 auto(或等价地只写 flex-shrink: 0,语义仍是「固定区」)。
  2. 「谁可以吃剩余空间,内容多了只在自己里面滚?」 → 这一块用 flex: 1 1 0 + min-height: 0

能稳定答这两句,就已经是在选布局算法而不是在随机试属性了。


小结

区域典型写法目的
父卡片height: 100% + flex + column占满外层给定高度,纵向排布
顶、标题、审核、底栏flex: 0 0 auto(或只写 flex-shrink: 0,本页等价)对应默认里的 shrink,改为不许压扁
中间可滚区flex: 1 1 0 + min-height: 0吃掉剩余高度,且允许比内容矮、内部滚动

记住一句:纵向「上中下」里,只有中间要吃剩余高度并滚动时,中间一定要 min-height: 0(或等价),否则 flex 会优先满足内容最小高度,把下面顶飞。


题外话:这和「会不会背 CSS」不是一回事

前端布局不是「会属性列表就自然会」;更像一套小型物理规则,要把产品意图翻译成正确的布局模型(谁固定、谁伸缩、谁滚动)。AI 能生成片段,但「默认 flex: 0 1 autoshrink: 1 会在空间不够时允许压扁谁」仍要人自己想清楚——这种推理到稳定实现的能力,和会不会失业的简单叙事无关,和单纯「复制一段 CSS」也不是同一回事。


「可以不手写,但不能不会」

以后真正吃香的不一定是「每一行都手写」,更常见的是:

  • 看得懂别人(含 AI)写了什么;
  • 能判断为什么这么写
  • 知道哪里可能有坑
  • 能把产品意图翻译成结构、状态、数据流和布局模型
  • 关键时候能接管、修正、取舍

「我可以不写,但不能不会」——说的正是这层:AI 会省掉大量重复劳动,但不会替代判断力。审核、推荐、训练数据、前后端闭环这类事,靠的是系统理解,不是单点抄一段样式。


推荐笔记模板(同类布局一条一记)

以后遇到「固定高 + 底栏常驻 + 中间滚」这类点,可以按下面骨架记笔记。攒几十条后,读 AI 产出会从「好像能跑」变成「知道为什么这么写、哪里不该这么写」,会稳很多。

md
## 问题

父容器固定高度,下面操作区要完整显示,上面内容区吃剩余高度。

## 关键写法

父容器:

```css
display: flex;
flex-direction: column;
```

自然高度区:

```css
flex: 0 0 auto;
```

剩余空间区:

```css
flex: 1 1 0;
min-height: 0;
```

再配合 `overflow: auto` 或小程序 `scroll-view` 等,只在中间区滚动。

## 为什么

Flex 子项默认 `min-height: auto`,内容过长时会按「内容最小高度」参与分配,容易把底部挤出可视区。`min-height: 0` 允许该区比内容更矮,超出部分在**本区内部**滚动。

## 口诀

固定区不缩,内容区吃剩余。能滚的区域:`flex: 1`(或 `flex: 1 1 0`)+ `min-height: 0`

本站总访问