最可靠解法是用@supports(min-height:100dvh)包裹min-height:100dvh并前置min-height:100vh兜底;100vh按物理屏高静态计算导致滚动时高度不更新,引发裁切、错位等问题;必须用min-height而非height,绝对定位元素需父容器设高度或改用top/bottom。

直接用 min-height: 100dvh,但必须包裹在 @supports (min-height: 100dvh) 里,并前置 min-height: 100vh 作为兜底——否则 iOS 15 及更早版本会完全塌陷,不是留白,是“看不见内容”。
为什么 100vh 在移动端会多出一截
它不是计算错了,而是算得太“老实”:浏览器按设备物理屏高(比如 iPhone 15 的 852px)算出 1vh = 8.52px,于是 100vh 固定为 852px。但用户刚打开页面时地址栏还挂着,实际可见高度可能只有 780px;滚动后地址栏收起,视口变大,100vh 却不更新,导致内容被切、fixed 元素错位、flex 居中失效。
典型现象包括:
-
overflow: hidden失效,意外出现滚动条 - 全屏弹窗底部被工具栏遮挡
-
position: fixed元素在滚动后偏移 -
calc(100vh - 60px)算出来的值比实际可用空间大
必须用 min-height 而不是 height
height: 100dvh 看似简洁,但风险极高:
立即学习“前端免费学习笔记(深入)”;
- Firefox 目前仍不支持
height: 100dvh(仅支持min-height) - iOS 15.x 及旧 WebView 会直接跳过整条规则,元素回退到
height: auto,视觉上“消失” - 内容超长时会被强制裁剪,用户滑不到底部
正确姿势是只用 min-height,让容器至少撑满可视区,又允许内容自然延伸:
.page {
min-height: 100vh;
}
@supports (min-height: 100dvh) {
.page {
min-height: 100dvh;
}
}@supports 必须包裹整个规则块
常见错误写法:min-height: 100vh; min-height: 100dvh; —— 这会让 Safari 16+ 依然走 100vh 分支,根本没触发 dvh。
真正生效的写法必须满足三个条件:
-
@supports检测的是属性+值整体,不能只包值 -
min-height: 100vh必须写在@supports外层,否则老设备连兜底都没有 - 不要在
@media查询里用100dvh,比如@media (min-height: 100dvh)是非法语法
绝对定位元素容易失效的隐藏坑
如果你给一个 position: absolute 元素设了 min-height: 100dvh,但它的父容器只是 position: relative 且没设高度,那它照样不会占满屏幕——dvh 依赖包含块的计算上下文,不是“全局窗口高度”。
稳妥解法:
- 确保父容器(如
body或直接祖先)也声明了min-height: 100dvh - 或改用
top: 0; bottom: 0;替代height/min-height,只要父容器是relative或fixed就能可靠撑满 - 别迷信
-webkit-fill-available:它只对height有效,min-height不认;软键盘弹起时不收缩;iPad Chrome 中甚至会叠加翻倍
最易被忽略的一点:哪怕你用了 100dvh,如果 html 或 body 有默认 margin(比如某些重置不彻底的 CSS),window.innerHeight 的取值就会不准,JS 动态方案也会失准——所以检查 html, body { margin: 0; } 是前提,不是可选项。


















