100vh在移动端因基于布局视口计算导致高度误差,ios safari加载时固定按物理屏高计算且不更新;唯一可靠解法是用@supports包裹的100dvh,老环境需js动态设--vh配合calc(--vh * 100)降级。

100vh 在移动端出现高度误差,不是你写错了单位,而是浏览器根本没按你“看到的屏幕”去算——它用的是布局视口(layout viewport),把地址栏、底部工具栏这些可隐藏区域全塞进去了。
100vh 为什么在 iOS Safari 里“算不准”
iOS Safari 加载页面时,会以设备物理屏幕高度为基准计算 100vh,比如 iPhone 14 的 844px。但此时地址栏和底部工具栏占着空间,真实可视区域可能只有约 700px。结果就是内容被遮住,或者底部大片留白。
更关键的是:这个值只在页面加载那一刻计算一次,之后无论地址栏收起、键盘弹出、横竖屏切换,100vh 都不会更新。
-
height: 100vh比min-height: 100vh更危险——内容超长时直接被裁剪,滚动都拉不到底部 - Android Chrome 表现稍好,但地址栏隐藏时仍会触发布局跳动
- 微信 WebView(基于旧 WKWebView)行为和 iOS Safari 15.x 一致,
100vh完全不可靠
@supports (min-height: 100dvh) 必须包裹整条规则
100dvh 是当前唯一语义正确、无需 JS 的现代解法,代表“当前可见区域高度”,iOS 16.4+、Chrome 105+、Firefox 112+ 已原生支持。但它必须用 @supports 正确降级,否则老环境直接失效。
错误写法:min-height: 100vh; min-height: 100dvh; —— 老 Safari 会忽略第二行,新 Safari 可能因解析顺序或构建工具干扰,依然走 100vh 分支。
正确写法是:
.page { min-height: 100vh; }
@supports (min-height: 100dvh) {
.page { min-height: 100dvh; }
}
-
@supports必须包裹整个 CSS 规则块,不能只包值 - 兜底声明必须写在
@supports外面,确保老环境至少有基础高度 - 别用 PostCSS 自动转换单位插件——它们常把
100dvh错误降级成100vh
position: absolute 元素设了 100dvh 还是没撑开
绝对定位元素的百分比高度(包括 100dvh)依赖包含块(containing block)的高度。如果父容器没设高度,100dvh 实际计算为 0。
常见于弹窗、遮罩层、底部固定按钮——它们的父容器(比如 body 或某个 .wrapper)没设 min-height。
- 别对绝对定位元素用
height: 100dvh,改用top: 0; bottom: 0;,更稳定 - 若父容器本身靠
--vh驱动,JS 初始化必须先于子元素渲染,否则读到的是初始1px - 用开发者工具选中该元素 →「Computed」→ 看
height值,如果是0px或auto,说明包含块断了
微信 WebView 和 iOS 15.x 必须用 JS 动态设 --vh
微信内置浏览器(X5 内核)和 iOS 15.x 及更早版本根本不支持 100dvh,CSS 方案在此类场景下完全失效。
可靠做法是监听 resize、scroll 和微信特有的 weixinjsbridgeReady,用 requestAnimationFrame 防抖更新 --vh 变量:
function setVh() {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
setVh();
window.addEventListener('resize', () => requestAnimationFrame(setVh));
window.addEventListener('scroll', () => requestAnimationFrame(setVh));
if (window.WeixinJSBridge) WeixinJSBridge.invoke('getNetworkType', setVh);
- CSS 中统一用
min-height: calc(var(--vh, 1px) * 100),不依赖父容器是否设了height: 100% - 首次执行必须在
DOMContentLoaded后立即进行,避免白屏或闪动 - 别漏掉安全区:
padding-bottom: env(safe-area-inset-bottom)要配合显式height: 100%才生效
真正麻烦的不是怎么写,而是不同环境对同一单位的响应逻辑完全不同:Safari 把地址栏算进去,Chrome 不算;微信 X5 内核不认 dvh,但又不报错;env(height) 只在 Safari 有效,safe-area-inset-bottom 在安卓上返回 0px…… 最稳妥的做法,永远是先判断运行环境,再决定用纯 CSS 还是加 JS 回退。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











