新视口单位 svh/dvh/lvh:移动端 100vh 的救赎
前两天在 iPhone 上验收一个新落地页,全屏首屏底部被 Safari 工具栏裁掉一截。2025 年了,这个上古问题居然还有新人踩——不过好消息是,现在终于不用写那个 --vh 的 JS hack 了,CSS 原生方案已经成熟可用。
100vh 的老毛病
移动端浏览器的地址栏和底部工具栏是会动态收起/展开的:
- 页面刚加载:地址栏 + 工具栏都在,可视区域小
- 用户往下滑:栏收起,可视区域变大
而 100vh 永远等于"栏都收起时"的最大高度。于是首屏用 height: 100vh 的元素,在栏展开时就会有一截被挡住。
三兄弟登场
CSS Values 4 定义了一组新视口单位,把"视口"拆成了三种状态:
| 单位 | 含义 | 对应状态 |
|---|---|---|
svh |
Small Viewport Height | 栏全部展开时的最小高度 |
lvh |
Large Viewport Height | 栏全部收起时的最大高度(= 旧的 vh) |
dvh |
Dynamic Viewport Height | 跟着栏的收展实时变化 |
配套的还有宽度方向 svw/lvw/dvw,以及 vmin/vmax 对应的动态版本,但日常 99% 的场景只用高度。
各适合什么场景
svh:保证内容永远完整可见
.hero {
/* 不管栏收不收,首屏一定完整展示,不会被裁 */
min-height: 100svh;
}
代价是栏收起后底部可能露出一点空白背景。适合首屏必须完整呈现的落地页、登录页。
dvh:内容跟着视口动
.chat-container {
/* 栏收展时高度实时跟随,永远铺满可见区域 */
height: 100dvh;
}
适合全屏应用容器:聊天界面、全屏地图。注意栏收展时元素会跟着伸缩,如果里面有 resize 监听的图表,要留意抖动。
lvh:就是旧 vh 的语义,一般用不到,除非你想明确表达"按最大高度算"。
迁移:渐进增强写法
老项目的 --vh hack 长这样(相信很多人项目里还有):
function setVh() {
document.documentElement.style.setProperty(
'--vh',
`${window.innerHeight * 0.01}px`
)
}
setVh()
window.addEventListener('resize', setVh)
.hero {
height: 100vh;
height: calc(var(--vh, 1vh) * 100);
}
现在可以直接用 CSS 的层叠回退,优雅得多:
.hero {
height: 100vh; /* 老浏览器兜底 */
height: 100svh; /* 支持的浏览器覆盖上一行 */
}
不支持新单位的浏览器会忽略第二行,自动用第一行。零 JS、零侵入、平滑过渡。
支持度现状(2025 年中)
- Safari 15.4+(2022 年 3 月)
- Chrome/Edge 108+(2022 年 12 月)
- Firefox 101+
主流覆盖率已经 95%+。还在维护 2022 年以前老浏览器兼容的项目,保留 100vh 兜底即可,成本为零。
小结
100vh在移动端是"栏收起后的最大高度",首屏会被工具栏裁svh永远完整可见(落地页首选);dvh实时跟随(全屏应用首选)- 迁移写法:
height: 100vh; height: 100svh;,一行兜底一行增强 - 终于可以删掉那个
--vh的 JS hack 了
移动端全屏布局这个困扰了前端快十年的问题,总算有了一个"本该如此"的解法。
看到这里了?如果这篇文章帮到过你
请我喝瓶可乐就好 🥤

评论0 条
还没有评论,来抢沙发~