答案是safari将地址栏高度计入100vh计算:初始时按物理屏高(如812px)算1vh=8.12px,100vh达812px,超出可见区域约50px;滚动后地址栏收起,视口变大但100vh不重算,导致内容裁切、底部留白或意外滚动条。

为什么100vh在iOS Safari里会多出滚动条
它不是你CSS写错了,是Safari把地址栏高度算进去了:刚加载时地址栏挂着,window.innerHeight可能是762px,但1vh却按设备物理屏高(比如812px)算成8.12px,于是100vh实际是812px——比你能看见的区域高约50px。滚动后地址栏收起,视口变大,但100vh不重算,内容就被切掉、底部留白,甚至触发意外滚动条。
- 典型现象包括:
overflow: hidden失效、position: fixed元素错位、全屏轮播图顶部被遮挡 -
height: 100%无效,除非html和body都显式设了height: 100%(它们默认是auto) - Android Chrome同样存在,尤其在PWA或全屏模式下更明显
@supports必须包裹整条规则,不能只包值
直接写min-height: 100vh; min-height: 100dvh;是错的——老Safari忽略第二行,新Safari仍用第一行,问题照旧。正确做法是用@supports包裹整个选择器和声明块,兜底声明必须写在外面。
- 错误写法:
.page { min-height: 100vh; min-height: 100dvh; } - 正确写法:
.page { min-height: 100vh; }@supports (min-height: 100dvh) { .page { min-height: 100dvh; } } - 必须用
min-height而非height:避免内容超长时被截断 - 别裸写
height: 100dvh,它会强制裁剪内容,用户滑不到底部
position: absolute元素套100dvh为啥没反应
绝对定位元素的百分比高度(包括100dvh)依赖包含块(containing block)的高度,不是“强制占满屏幕”。常见于弹窗、遮罩层、底部固定按钮。
- 父容器(如
body或直接祖先)没设min-height: 100dvh或显式高度 → 子元素100dvh实际计算为0 - 别对绝对定位元素用
height: 100dvh,改用top: 0; bottom: 0;更可靠 - 若父容器本身也靠
--vh驱动,需确保它先完成初始化,否则子元素读到的是初始1px
兼容iOS 15及微信WebView必须用JS动态设--vh
微信内置浏览器、iOS 15.x等环境根本不认dvh或svh,CSS方案在此类场景下完全失效。此时唯一可靠路径是监听resize(不是scroll),动态设置CSS变量。
- 必须在
DOMContentLoaded后立即执行一次,否则首屏渲染用的是地址栏未收起时的错误初始值 - 监听
resize就够了,orientationchange在部分iOS版本中不可靠;地址栏收放、键盘弹起都会触发resize - 要用
requestAnimationFrame防抖,iOS连续触发resize,不节流会导致频繁重排 - CSS中统一用
min-height: calc(var(--vh, 1px) * 100),不依赖父容器是否设了height: 100%
真实项目里最容易被忽略的,是@supports只包声明值、没包整条规则,以及iOS 15以下用户根本看不到100dvh生效——这时候JS兜底不是备选,是必做。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











