100vh在移动端显示异常是因为其按初始带地址栏的布局视口静态计算,不随滚动时地址栏收起动态更新,导致内容遮挡、留白或错位;应改用min-height: 100dvh并用@supports包裹整条规则降级,不支持时需js动态设--vh变量。

100vh 在移动端“高度不对”,不是你漏写了 height: 100% 或忘了设 html、body 高度,而是浏览器根本没按你此刻看到的区域算——它锁死了初始加载时带地址栏的视口高度,后续滚动收起地址栏,100vh 的值纹丝不动。
为什么 100vh 在 iOS Safari 和微信里总差一截
iPhone 加载页面时,Safari 把地址栏(约 50–80px)也算进“布局视口”,于是 1vh 按物理屏高(比如 844px)算成 8.44px,100vh 就是 844px;但你能看见的区域只有约 760px。结果就是:内容被顶部遮住、底部留白、position: fixed 元素错位、轮播图切掉第一张。
- iOS 15.x 及更早、微信 X5 内核 WebView 完全不支持
100dvh,写上去等于没写 - Android Chrome 在 PWA 或全屏模式下同样会复现,只是表现略隐蔽
-
height: 100vh比min-height: 100vh更危险:前者强制裁剪,用户滑不到底部
用 100dvh 替代前必须做对三件事
直接把 min-height: 100vh 换成 min-height: 100dvh 不行,老浏览器会忽略整条规则,新浏览器也可能因解析顺序或构建工具干扰继续走 100vh 分支。
- 降级声明必须写在
@supports外面:.page { min-height: 100vh; } -
@supports必须包裹整个规则块,不能只包值:@supports (min-height: 100dvh) { .page { min-height: 100dvh; } } - 永远优先用
min-height,不用height:避免内容超长时被截断 - PostCSS 插件若自动把
dvh降级为vh,必须关掉——否则白忙一场
position: absolute 元素设了 100dvh 还是没撑开
绝对定位元素的百分比高度(包括 100dvh)依赖包含块(containing block)的高度,不是“强制占满屏幕”。常见于弹窗、遮罩层、底部按钮。
- 父容器(如
body或直接祖先)没设min-height: 100dvh或显式高度 → 子元素100dvh实际计算为 0 - 别对绝对定位元素用
height: 100dvh,改用top: 0; bottom: 0;,更可靠 - 如果父容器本身靠
--vh驱动,JS 初始化必须先于子元素渲染,否则读到的是初始 1px
兼容 iOS 15 及微信 WebView?JS 动态设 --vh 是唯一解
这些环境根本不识别 dvh,CSS 方案完全失效。必须用 JS 读取 window.innerHeight 并写入 CSS 变量,但两个点极易漏掉:
- 首次执行必须在
DOMContentLoaded后立即同步运行,否则首屏渲染用的是地址栏未收起时的错误初始值 - 监听目标不能只用
resize:iOS 地址栏收起时不触发resize,只触发scroll;微信还可能需要监听weixinjsbridgeReady - 防抖必须用
requestAnimationFrame,而非setTimeout:iOS 连续触发resize,节流不当会导致高度卡死 - CSS 中统一用
min-height: calc(var(--vh, 1px) * 100),不依赖父容器是否设了height: 100%
真正容易被忽略的是:即使你用了 100dvh,也要检查父容器有没有参与高度继承链;而一旦引入 JS 方案,requestAnimationFrame 防抖和首次同步执行这两个点,漏掉任何一个都会导致首屏白屏或滚动跳变。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











