100vh 在 iOS Safari 等移动端浏览器中“不够高”是因为其基于 layout viewport 计算,不随地址栏隐藏而更新;应优先使用响应 visual viewport 的 100dvh,降级用 @supports 或 visualViewport API 动态设置 CSS 变量。

为什么 100vh 在 iOS Safari 和部分 Android 浏览器里“不够高”
移动端浏览器(尤其是 Safari)的地址栏会动态收起/展开,vh 单位在页面加载时按「当前可视窗口高度」计算,但这个高度包含地址栏——当用户滚动后地址栏隐藏,实际可视区域变大,100vh 却没重算,导致底部内容被“顶出”或留白。这不是 bug,是规范行为:CSS 视口单位基于 layout viewport,而非 visual viewport。
用 100dvh 替代 100vh(现代方案)
100dvh 是「dynamic viewport height」,它响应 visual viewport 变化,地址栏收起时自动拉伸。目前 Chrome 105+、Safari 16.4+、Firefox 112+ 均支持。
实操建议:
- 直接把
height: 100vh换成height: 100dvh,无需 JS 干预 - 加一层降级:用
@supports判断,不支持时回退到100vh或 JS 补偿 - 注意:Safari 16.4 之前版本(如 iOS 16.3)不支持,需单独处理
.full-height {
height: 100vh; /* fallback */
height: 100dvh;
}
<p>@supports (height: 100dvh) {
.full-height {
height: 100dvh;
}
}</p>
用 visualViewport API + JS 动态更新(兼容旧版 Safari)
对 100dvh 不支持的环境(如 iOS visualViewport 的 resize 事件,手动设置自定义 CSS 变量。
关键点:
- 必须在
DOMContentLoaded后执行,否则visualViewport可能未就绪 - 只在移动端初始化,桌面端没必要
- 避免高频触发重排:用
requestAnimationFrame节流 - 初始值也要设一次,否则首屏可能错位
if ('visualViewport' in window) {
const setDvh = () => {
document.documentElement.style.setProperty(
'--dvh',
`${window.visualViewport.height}px`
);
};
setDvh();
window.visualViewport.addEventListener('resize', setDvh);
}
然后在 CSS 中使用:height: calc(var(--dvh, 100vh));
别忽略的细节:viewport meta 标签和缩放
如果页面允许用户缩放(user-scalable=yes),visualViewport.height 会随缩放比例变化,而 dvh 不会——这时两者行为不一致。
稳妥做法:
- 强制禁用缩放:
- 若必须支持缩放,优先用 JS 方案并监听
scale变化 - 测试真机:模拟器里的
visualViewport行为常与真机不一致,尤其 iOS
真正麻烦的不是写法,而是不同 iOS 版本对 visualViewport 的实现差异——比如 iOS 15.4 有 resize 延迟,iOS 16.0 修复了但引入了 orientation 切换时的抖动。得靠真机反复验证。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











