最可靠解法是用 @supports 包裹整条规则并外置 min-height: 100vh 作降级;绝对定位元素需用 top/bottom 替代 height;ios 15/微信 webview 必须 js 动态设 --vh 并用 raf 防抖且首次同步执行。

直接用 min-height: 100dvh 并配合 @supports 降级,是当前最轻量、最可靠的解法;但裸写或写错包裹方式,反而会让新旧 Safari 都失效。
@supports 必须包裹整条 CSS 规则块
老版 Safari(iOS 15.x 及更早)、微信 X5 内核等环境完全不识别 100dvh,会直接忽略该声明。如果只写:
.page { min-height: 100vh; min-height: 100dvh; }
结果是所有 Safari 都走第一行,问题照旧。正确做法是:
-
@supports必须包裹整个选择器和声明块,不能只包值 - 兜底的
min-height: 100vh必须写在@supports外面,确保老环境至少有基础高度 - 构建工具(如 PostCSS)若自动把
dvh降级成vh,必须关掉该功能
绝对定位元素设了 100dvh 还是没撑开
position: absolute 元素的百分比高度(包括 100dvh)依赖包含块(containing block)的高度,不是“强制占满屏幕”。常见于弹窗、遮罩层、底部固定按钮。
- 父容器(如
body或直接祖先)没设min-height: 100dvh或显式高度 → 子元素100dvh实际计算为0 - 别对绝对定位元素用
height: 100dvh,改用top: 0; bottom: 0;更可靠 - 若父容器本身也靠
--vh驱动,需确保它先完成初始化,否则子元素读到的是初始1px
需要兼容 iOS 15 及微信 WebView?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 防抖和首次同步执行这两个点,漏掉任何一个都会导致首屏白屏或滚动跳变。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











