100vh在iOS Safari中比实际可视区域高,因其按初始布局视口(含地址栏)计算1vh,滚动后地址栏收起但值不变,导致高度固定而可视区域变大;应优先使用@supports检测的100dvh,并降级至100vh或JS动态--vh变量。

为什么100vh在iOS Safari里总比实际可视区域高
因为1vh是按“初始布局视口”算的,不是你滚动时眼睛看到的高度。iOS Safari加载页面那一刻,地址栏还在,它就把整个设备高度(含地址栏)当视口,算出1vh ≈ 8.12px(以iPhone 13为例),后面哪怕地址栏收起、可用高度多了50px,1vh值也不变——height: 100vh就永远卡在812px,而你真正能看见的只有762px,底部必然被截或留白。
直接用100dvh替换100vh是否安全
安全,但必须加@supports兜底,不能裸写:
-
@supports (height: 100dvh)是唯一可靠的检测方式,100dvh在不支持的浏览器中会被整条声明忽略,不会报错也不会生效 - 务必把
height: 100vh写在@supports块外面,作为降级 fallback;否则老 iOS 会直接跳过整条规则 - 不要写成
height: 100vh, 100dvh——CSS语法不支持逗号分隔单位,这行会完全失效 - iOS 16.4+、Chrome 109+ 原生支持;微信iOS ≥ 8.0.49(内核已升至Safari 16.4)也支持;但微信安卓仍大量跑X5旧内核,
@supports会直接 fallback
哪些场景必须用JS回退而不是只靠dvh
当你需要兼容 iOS 15 及更早版本,或微信安卓(尤其X5 v6.x内核)时,100dvh形同虚设,必须用 JS 动态注入--vh变量:
- 监听
resize和scroll事件——iOS 地址栏收起/展开时,scroll比resize更可靠 - 首次渲染前就要执行一次
setVh(),否则白屏或闪动;放在<head>内联 script 最稳 - 防抖必须用
requestAnimationFrame,setTimeout在微信安卓下容易卡死 - CSS里统一写
height: calc(var(--vh, 1vh) * 100),其中1vh是保底值,不是精确 fallback
绝对定位容器里用100dvh为什么还是错位
因为position: absolute元素的百分比高度(包括100dvh)依赖包含块高度,而如果父容器没设height,它的高度就是auto,此时100dvh虽然数值对了,但计算基准仍是错误的包含块。
立即学习“前端免费学习笔记(深入)”;
解决办法只有两个:
- 给最近的
position: relative祖先加height: 100%,并确保其父链上(至少html和body)有显式height: 100% - 或者干脆不用
dvh,改用min-height: 100dvh+height: fit-content,让内容撑开,避免依赖父容器高度
真正容易被忽略的是:问题不在dvh本身,而在于你默认它能绕过 CSS 定位体系的约束——它不能。


















