最可靠解法是用 @supports 包裹整条 min-height: 100dvh 规则,外层保留 min-height: 100vh 降级;绝对定位元素需用 top/bottom 替代 height;iOS 15-需 JS 动态设 --vh 并用 requestAnimationFrame 防抖且首次同步执行。

直接用 min-height: 100dvh 并配合 @supports 降级,是当前最轻量、最可靠的解法;但裸写或写错包裹方式,反而会让新旧 Safari 都失效。
为什么 100dvh 必须用 @supports 包裹整条规则
iOS Safari 16.4+ 和 Chrome 105+ 才原生支持 100dvh,旧版(尤其是 iOS 15.x 及微信 WebView)会直接忽略该声明。如果只写:
.page {
min-height: 100vh;
min-height: 100dvh; /* 错误:老 Safari 忽略这行,新 Safari 仍用上一行 */
}结果是所有 Safari 都走 100vh,问题照旧。
-
@supports必须包裹整个 CSS 规则块,不能只包值 - 兜底声明(
min-height: 100vh)必须写在@supports外面,确保老环境至少有基础高度 - 优先用
min-height而非height,避免内容超长时被截断
position: absolute 元素设了 100dvh 还是没撑开
绝对定位元素的百分比高度(包括 100dvh)依赖包含块(containing block)的高度,不是“强制占满屏幕”。常见于弹窗、遮罩层、底部固定按钮。
立即学习“前端免费学习笔记(深入)”;
- 父容器(如
body或直接祖先)没设min-height: 100dvh或显式高度 → 子元素100dvh实际计算为 0 - 别对绝对定位元素用
height: 100dvh,改用top: 0; bottom: 0;更可靠 - 若父容器本身也靠
--vh驱动,需确保它先完成初始化,否则子元素读到的是初始 1px
需要兼容 iOS 15 及以下?JS 动态设 --vh 是唯一解
此时 100dvh 完全不识别,页面会塌成一条线。必须用 JS 动态读取 window.innerHeight 并写入 CSS 变量:
- 必须在
DOMContentLoaded后立即执行一次,否则首屏渲染用的是地址栏未收起时的错误初始值 - 监听
resize、scroll(iOS 地址栏收起时仅触发scroll),以及微信特有的weixinjsbridgeReady - 防抖必须用
requestAnimationFrame,而非setTimeout:微信安卓下resize触发极快且无规律,节流不当会导致高度卡死 - CSS 中统一用
min-height: calc(var(--vh, 1px) * 100),不依赖父容器是否设了height: 100%
真正容易被忽略的是:即使你用了 100dvh,也要检查父容器有没有参与高度继承链;而一旦引入 JS 方案,requestAnimationFrame 防抖和首次同步执行这两个点,漏掉任何一个都会导致首屏白屏或滚动跳变。


















