100vh 在 ios safari 中“算不准”是因为它基于布局视口而非实际可视区域,且加载后不再响应地址栏、键盘等变化;推荐优先使用 100dvh 并配合 @supports 正确降级,或在旧版 ios 中用 js 动态设置 --vh 变量。

100vh 在 iOS Safari 里为什么“算不准”
因为 100vh 拿的是布局视口(Layout Viewport)高度,不是用户实际看到的区域。iPhone Safari 初始加载时,地址栏和底部工具栏都占空间,但 100vh 却按“全屏物理高度”计算——比如 iPhone 14 的 844px,而真实可视高度只有约 700px。结果就是内容被工具栏遮住,或者留白。
更麻烦的是:这个值在页面加载那一刻就锁死了,后续地址栏收起/弹出、键盘唤起,100vh 完全不响应变化。
- Android Chrome 表现稍好,但地址栏隐藏时仍会触发布局跳动
- 微信 WebView(基于旧版 WKWebView)行为和 iOS Safari 15.x 一致,
100vh无效且不可靠 -
height: 100vh比min-height: 100vh更危险——可能直接截断内容,滚动都拉不到底部
@supports (min-height: 100dvh) 必须包裹整条规则
直接写 min-height: 100vh; min-height: 100dvh; 是错的。老 Safari(iOS 15.x 及更早)不认识 100dvh,会忽略整行,但保留上一行 100vh,问题照旧;新 Safari(iOS 16.4+)则可能因解析顺序或构建工具干扰,依然走 100vh 分支。
正确写法是把降级声明放在外面,@supports 里面只放新单位:
.page {
min-height: 100vh;
}
@supports (min-height: 100dvh) {
.page {
min-height: 100dvh;
}
}
- 不能只包值,必须包整个选择器块
- 别用 PostCSS 自动转换单位插件——它们常把
100dvh错误降级成100vh -
min-height优于height,防止内容超长时被裁剪
position: absolute 元素设了 100dvh 还是没撑开
绝对定位元素的百分比高度(包括 100dvh)依赖包含块高度。如果父容器没设高度,100dvh 实际计算为 0。
- 常见于弹窗、遮罩层、底部固定按钮——它们的父容器(比如
body或某个 wrapper)没设min-height - 别对绝对定位元素用
height: 100dvh,改用top: 0; bottom: 0;,更稳定 - 若父容器本身靠
--vh驱动,JS 初始化必须先于子元素渲染,否则读到的是初始 1px
需要兼容 iOS 15 及更早?JS 设置 --vh 是唯一解
此时 100dvh 完全不识别,页面会塌成一条线。必须用 JS 动态读取 window.innerHeight 并写入 CSS 变量:
function setVh() {
document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
}
setVh();
window.addEventListener('resize', setVh);
window.addEventListener('orientationchange', setVh);
CSS 中用:min-height: calc(var(--vh, 1px) * 100);
- 必须在
DOMContentLoaded后立即执行一次,否则首屏渲染用的是地址栏未收起时的错误初始值 - resize 事件在 iOS 上高频触发,不加防抖或
requestAnimationFrame节流,会导致重排卡顿 - 别指望
height: 100%或flex: 1替代——它们依赖祖先高度,而移动端html/body默认无显式高度,根本链不起来
真正容易被忽略的点是:动态视口单位不是“写了就能用”,而是要分场景选 dvh(当前可见)、svh(地址栏展开时)、lvh(最大可用),并且 JS 回退必须覆盖首次渲染、resize、横竖屏三类时机。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











