100dvh是唯一语义正确、无需js的现代解法,它代表当前可见视口高度,ios 16.4+/chrome 105+/firefox 112+已原生支持;必须用min-height配合@supports检测,禁用height:100dvh及@media中使用,并彻底弃用-webkit-fill-available。

100vh 在移动端包含工具栏高度,不是你写错了,是浏览器按「布局视口(layout viewport)」算的——它把地址栏、底部导航栏这些可隐藏区域全算进去了,而用户滚动时看到的只是「视觉视口(visual viewport)」。结果就是:页面刚打开时 100vh 过高,底部留白;滚动后地址栏收起,100vh 却不更新,内容被截或突然上移。
100dvh 是唯一语义正确、无需 JS 的现代解法
100dvh(dynamic viewport height)代表“当前可见区域高度”,由浏览器原生响应地址栏显隐,iOS 16.4+、Chrome 105+、Firefox 112+、Edge 110+ 全已支持。
- 必须用
min-height,别用height:防止内容超长时被裁剪 - 声明顺序很重要:
min-height: 100vh必须写在前,@supports内覆盖它 -
@supports (min-height: 100dvh)是必要包裹,否则老 Safari(如 iOS 15)会直接忽略整条规则
.page {
min-height: 100vh;
}
@supports (min-height: 100dvh) {
.page {
min-height: 100dvh;
}
}
- 不要写成
height: 100dvh—— 它不随内容自适应,一屏装不下就永远滑不到下面 -
100dvh不能用于@media查询(比如@media (min-height: 100dvh)无效),这点和vh一致
-webkit-fill-available 根本不能修 100vh 问题
这个值常被误传为“撑满可见区”的银弹,但它在 height 上完全不可靠:
- 它是 WebKit 为
<input>等表单控件设计的内部填充逻辑,不是通用高度单位 - iOS Safari 对
height: -webkit-fill-available支持极不稳定:某些版本退化为auto,触发意外重排 - 完全不响应地址栏收起/展开,和
100vh一样“冻结”在初始值 - Chrome / Android WebView 直接忽略该值用于
height,CSS 声明失效
所以:height: -webkit-fill-available ≠ 100dvh,也不是兼容方案,纯属误导。
微信、iOS 15 及更早必须用 JS 动态方案
微信内置浏览器(X5 内核)和 iOS 15.x 不支持 100dvh,且 @supports 检测可能直接跳过——这时只能靠 JS 注入变量:
- 首次执行必须在
DOMContentLoaded前或同步运行,否则白屏阶段高度就错 - 监听
resize、scroll(iOS 地址栏收起只触发scroll)、orientationchange,微信还需监听weixinjsbridgeReady - 更新必须用
requestAnimationFrame包裹,否则 iOS 高频resize会强制同步布局、卡顿明显 - CSS 中写
min-height: calc(var(--vh, 1px) * 100),单位必须是1px,写成1vh会导致 fallback 失效
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));
真正容易被忽略的点有两个:一是 viewport-fit=cover 必须写在 <meta name="viewport"> 里,否则部分 Android 浏览器会冻结视口尺寸,连 100dvh 都不更新;二是所有 JS 方案里,1px 这个 fallback 单位漏掉一个字符,整个降级就崩了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











