← 返回文章列表
CSS·136 次阅读

新视口单位 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 条

还没有评论,来抢沙发~

发表评论

评论需审核后显示

© 2026 许冬博客 · 专注 JS 技术分享

·RSS 订阅·Powered by Nuxt 4