移动端 vh 不准因 ios safari 和部分 android 浏览器地址栏切换时视口高度动态变化而 vh 不重算;100dvh 可解决但需现代浏览器支持,旧版需 js 动态设置 --vh 变量并监听 resize 事件。

移动端容器高度用 vh 为什么经常不准
因为 iOS Safari 和部分 Android 浏览器在地址栏展开/收起时会动态改变视口高度,但 vh 单位不会实时重算——它基于初始加载时的视口,不是“当前可见区域”。比如页面刚打开时地址栏显示,100vh 对应的是含地址栏的高度;滚动后地址栏隐藏,可视区域变高,但 100vh 值不变,导致容器留白或溢出。
用 100dvh 替代 100vh 的前提和限制
100dvh(dynamic viewport height)是为解决上述问题引入的新单位,代表“当前实际可视区域高度”,现代 Chrome、Edge、Safari 16.4+ 已支持。但它不是万能解:
- 旧版 Safari(dvh,会直接回退到
height: auto或忽略整条声明 - 不能和
min-height混用做兜底:例如min-height: 100dvh在不支持的浏览器中会被整个丢弃,导致容器塌陷 - SSR 渲染时服务端无法获取设备信息,
dvh无法参与初始计算,首屏可能闪动
兼容性 fallback 方案:CSS 自定义属性 + JS 动态更新
最稳妥的做法是用 JS 获取 window.innerHeight,写入 CSS 自定义属性,再在 CSS 中引用。这样既能响应地址栏变化,又可控降级:
document.addEventListener('resize', () => {
document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
});
然后在 CSS 中这样用:
.full-height {
height: calc(var(--vh, 1vh) * 100);
}
- 注意:必须监听
resize,iOS 上地址栏切换会触发该事件;Android 部分机型还需监听orientationchange -
var(--vh, 1vh)中的1vh是降级值,确保不支持 JS 或未执行时至少有基础比例参考 - 避免在
scroll中频繁设置,resize已足够覆盖视口尺寸变化场景
绝对定位容器慎用 vh 类单位
如果容器是 position: absolute 且父容器没有显式高度,100vh 或 100dvh 仍可能失效——因为绝对定位元素的百分比高度依赖于包含块高度,而包含块若未设高,100% 就是 auto,此时 vh 单位虽生效,但布局逻辑已脱离预期。
- 解决方案:给最近的相对定位祖先加
height: 100%,并确保其父链上至少有一个明确高度来源(如html, body { height: 100% }) - 更简单的方式:直接对绝对定位元素用
top: 0; bottom: 0;,绕过高度计算依赖 - 注意
safe-area-inset-bottom:刘海屏/小黑条区域会让bottom: 0覆盖内容,需配合padding-bottom: env(safe-area-inset-bottom)
实际项目里,dvh 看似简洁,但真正在意首屏体验和低端机型覆盖时,JS + 自定义属性仍是更可控的选择。别忘了测试微信内置浏览器和 QQ 浏览器 X5 内核——它们对新单位的支持常滞后一到两个大版本。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











