最可靠解法是用@supports包裹min-height:100dvh,外层兜底min-height:100vh;因100vh基于含地址栏的布局视口,滚动后可视区变化但不重算,导致截断或留白;旧版ios/微信内核不识别dvh,需js动态注入--vh变量并监听scroll/weixinjsbridgeready。

直接用 min-height: 100dvh 替代 100vh,并用 @supports 包裹整条规则块——这是当前最轻量、最可靠的解法。裸写 100dvh 或只降级到 100vh,反而会让 iOS 15.x、微信 X5 内核等环境彻底失效。
为什么 100vh 在移动端“高度不准”
它不是 bug,是规范行为:100vh 基于初始布局视口(Layout Viewport),包含地址栏、工具栏高度;而用户滚动后地址栏收起,可视区域变高,100vh 却不会重算。结果就是:刚加载时内容被截断,下拉后底部留白,或固定按钮被遮挡。
- iOS Safari 和微信安卓 WebView 是重灾区,Chrome 移动端也存在类似问题
-
height: 100vh比min-height: 100vh更危险——可能强制裁剪内容 - 别指望
100% + height: 100%或flex: 1能绕过这个问题,它们最终仍依赖父容器高度,而父容器本身就被100vh错误计算拖累
用 100dvh 必须配 @supports 整块包裹
旧版浏览器(如 iOS 15.x、微信 X5)根本不识别 dvh 单位,遇到就跳过整条声明。如果你只写:
.page { min-height: 100vh; min-height: 100dvh; }
老环境忽略第二行,新环境仍走第一行,问题照旧。
- 正确写法是:
@supports (min-height: 100dvh) { .page { min-height: 100dvh; } } - 兜底的
min-height: 100vh必须写在@supports外层,确保所有浏览器至少有基础高度 - 构建工具(如 PostCSS 插件)若自动把
dvh降级成vh,必须关掉该功能,否则等于白写
绝对定位元素设了 100dvh 还是没撑开?检查包含块
position: absolute 元素的百分比高度(包括 100dvh)依赖其包含块(containing block)的高度,不是“强制占满屏幕”。常见于弹窗、遮罩层、底部按钮。
- 父容器(如
body或直接祖先)没设min-height: 100dvh→ 子元素100dvh实际计算为 0 - 优先改用
top: 0; bottom: 0;,比height: 100dvh更可靠 - 若父容器本身靠 JS 注入的
--vh变量驱动,需确保它先完成初始化,否则子元素读到的是初始1px
需要兼容 iOS 15 或微信 WebView?JS 动态设 --vh 是唯一解
这些环境完全不识别 dvh 或 svh,CSS 方案彻底失效。必须用 JS 读取 window.innerHeight 并注入 CSS 变量:
- 必须在
DOMContentLoaded后立即执行一次,否则首屏渲染用的是地址栏未收起时的错误初始值 - 监听目标不止
resize:iOS 地址栏收起只触发scroll,微信还要监听weixinjsbridgeReady - 防抖必须用
requestAnimationFrame,setTimeout在微信安卓下极易卡死 - CSS 中统一写
min-height: calc(var(--vh, 1px) * 100),不依赖父级是否设了height: 100%
真正容易被忽略的是:即使用了 100dvh,也要检查父容器是否参与高度继承链;而一旦引入 JS 方案,requestAnimationFrame 防抖和首次同步执行这两个点,漏掉任何一个都会导致首屏白屏或滚动跳变。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











