前端高度与滚动布局笔记(Grid/Flex + min-height: 0)
1. 核心结论(先记住这 6 条)
1fr 是“分剩余空间”,但在很多 Grid 场景里会被内容最小尺寸反向撑开。
- 需要“内部滚动”的布局,常规安全写法是
minmax(0, 1fr)(Grid)+ min-height: 0(子项)。
- 在
flex-direction: column 中,需要滚动的那一层通常要写 flex: 1; min-height: 0; overflow: auto;。
- 在
display: grid 中,需要滚动的行通常写 grid-template-rows: auto minmax(0, 1fr);。
- Ant Design 组件经常有中间包裹层(
spin-container / content-holder),滚动要放到“真实内容层”。
- 负
margin(例如 margin-top: -12px)在滚动布局里很容易造成遮挡假象,尽量避免。
2. 1fr 到底是什么
fr = fraction,按比例分配“剩余空间”。
grid-template-rows: auto 1fr 的意思是:第一行按内容,第二行吃剩余。
- 问题点:第二行实际最小尺寸常受内容影响,内容太多时会把行高顶大。
- 因此做滚动布局时,推荐把
1fr 写成 minmax(0, 1fr),明确允许它缩小到 0。
3. 为什么你“去掉固定高度”仍然会溢出
- 你只改了子层,父层仍允许内容撑开。
- 高度链路有一层没写
min-height: 0,滚动层就拿不到可压缩空间。
- Grid 第二行若是
1fr 而不是 minmax(0,1fr),会被内容反向顶高。
- Tabs/Spin 等中间层未约束时,
overflow: auto 看起来“失效”。
4. 常见场景标准写法
4.1 Flex 两行(头部固定 + 内容滚动)
.page {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.header {
flex-shrink: 0;
}
.body {
flex: 1;
min-height: 0;
overflow: auto;
}
4.2 Grid 两行(第一行自适应 + 第二行占满并滚动)
.grid {
height: 100%;
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
}
.grid-item {
min-height: 0;
min-width: 0;
}
.scroll-area {
height: 100%;
min-height: 0;
overflow: auto;
}
4.3 Antd Tabs 容器(非常常见)
.tabs-wrap {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.tabs-wrap .ant-tabs {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
}
.tabs-wrap .ant-tabs-content-holder {
flex: 1;
min-height: 0;
overflow: hidden;
}
.tabs-wrap .ant-tabs-content {
height: 100%;
min-height: 0;
}
.tabs-wrap .ant-tabs-tabpane {
height: 100%;
min-height: 0;
}
4.4 Antd List + Spin(你这次的关键点)
.list-shell {
width: 100%;
height: 0;
flex: 1;
min-height: 0;
overflow: hidden;
display: flex;
}
.list-shell .ant-spin-nested-loading {
width: 100%;
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
}
.list-shell .ant-spin-container {
height: 100%;
min-height: 0;
overflow: auto;
}
4.5 Antd Table(只 body 滚动)
.table-wrap {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.table-wrap .ant-table-wrapper,
.table-wrap .ant-spin-nested-loading,
.table-wrap .ant-spin-container,
.table-wrap .ant-table,
.table-wrap .ant-table-container {
min-height: 0;
}
.table-wrap .ant-table-body {
overflow: auto;
}
5. 日常推荐模板(可直接套)
.layout-root {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.layout-main {
flex: 1;
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
}
.layout-main-item {
min-height: 0;
min-width: 0;
}
.layout-scroll {
height: 100%;
min-height: 0;
overflow: auto;
}
6. 常见误区
- 误区:只要给子元素
overflow: auto 就能滚。
- 事实:父链路不收缩时,子元素不会滚,只会继续撑开。
- 误区:
1fr 一定等于“固定剩余高度”。
- 事实:如果没做
minmax(0,1fr),内容可能反向影响它。
- 误区:
height: 100% 一定有效。
- 事实:祖先没有明确高度时,
height: 100% 会失效或不可控。
- 误区:负
margin 只是视觉微调。
- 事实:在滚动容器中容易造成遮挡和可视区错觉。
7. 排障流程(5 分钟版)
- 先定位“你期望滚动的层”是哪一层。
- 向上检查每一层是否是
flex/grid 子项。
- 这些子项补
min-height: 0(横向问题补 min-width: 0)。
- Grid 场景把
1fr 改成 minmax(0,1fr)。
- 清理负
margin、position 偏移、max-height 冲突。
- 再检查滚动是否在真实内容层(Antd 常是
.ant-spin-container / .ant-table-body)。
- 在 Elements 面板选中目标滚动层,看
Computed 的 height 与 overflow。
- 按父链逐层看
display、min-height、height。
- 临时加调试样式:
* { outline: 1px solid rgba(255, 0, 0, 0.15); }
- 临时给可疑层加:
min-height: 0 !important;
overflow: hidden !important;
- 若问题消失,说明就是该层约束链断了。
9. 经验结论(可背)
- 想让内部滚动,先保证“外层可收缩”。
- Grid 第二行可滚动,优先
minmax(0, 1fr)。
- Flex 纵向第二行可滚动,优先
flex:1 + min-height:0 + overflow:auto。
- Antd 场景多检查中间包装层,不只看你写的组件根节点。