Back

理解 CSS 中的动态视口单位

理解 CSS 中的动态视口单位

如果你曾经在移动端构建全屏布局,并注意到内容被浏览器地址栏遮挡,那么你就遇到了经典的 100vh 问题。过去的解决方案需要使用 JavaScript 技巧。现在,CSS 通过现代视口单位原生支持这一功能:svhlvhdvh

核心要点

  • 传统的 vh 单位在移动端映射到大视口,当浏览器地址栏可见时会导致内容溢出。
  • CSS 现在定义了三种视口状态——大视口(lvh)、小视口(svh)和动态视口(dvh)——每种都有自己的单位集。
  • 对首屏内容使用 svh,对沉浸式全屏布局使用 lvh,对响应浏览器 UI 变化的自适应设计使用 dvh
  • 动态视口单位不考虑虚拟键盘;在真实设备上测试输入密集型布局。
  • 始终为不支持新单位的旧版浏览器提供 vh 降级方案。

为什么 vh 在移动端会失效

在桌面端,vh 工作可靠。在移动端,浏览器的 UI——地址栏、标签栏、导航控件——会随着用户滚动而展开和收起。视口高度会变化,但 vh 不会跟随它。

vh 反映的是大视口(类似于 lvh),它假设浏览器界面完全收起。因此,当地址栏可见时,100vh 元素可能会溢出可见区域。内容被截断,按钮无法触及,布局被破坏。

三种视口状态:大视口、小视口和动态视口

CSS Values and Units 规范定义了三种不同的视口状态来解决这个问题:

  • 大视口 — 所有浏览器 UI 收起时测量(最大可用高度)
  • 小视口 — 所有浏览器 UI 展开时测量(最小可用高度)
  • 动态视口 — 跟踪当前活动的状态

每种状态都有对应的 CSS 单位集:

视口状态高度单位宽度单位
大视口lvhlvw
小视口svhsvw
动态视口dvhdvw

每个系列还包括 minmaxinlineblock 变体(dvidvbsvminlvmax 等)。

重要提示: 在现代浏览器中,vh 实际上映射到 lvh。它们都反映大视口尺寸。

在没有动态 UI 的桌面浏览器上,所有三种视口尺寸都是相同的。

何时使用 svhlvhdvh

使用 svh 当内容必须在页面初始加载时完全可见,在任何滚动发生之前。它保证你的布局适合最小可能的视口——浏览器界面完全可见。

/* 内容在加载时始终可见,无需滚动 */
.above-the-fold {
  min-height: 100svh;
}

使用 lvh 用于沉浸式全屏体验——游戏、启动画面或应用外壳——你希望填充最大可用空间。

/* 浏览器界面隐藏时填充屏幕 */
.fullscreen-app {
  height: 100lvh;
}

使用 dvh 当你希望布局随着浏览器 UI 变化而自适应。这是大多数响应式布局最实用的选择。

/* 在移动端有问题 */
.hero { height: 100vh; }

/* 适应浏览器界面变化 */
.hero {
  height: 100vh;   /* 旧版浏览器的降级方案 */
  height: 100dvh;  /* 现代浏览器使用这个 */
}

上述降级模式有效,因为不识别 dvh 的浏览器会忽略第二个声明并应用第一个。现代浏览器应用最后一个有效声明,因此 100dvh 生效。

虚拟键盘的注意事项

dvh 处理地址栏调整大小,但不处理虚拟键盘。当用户点击输入框并出现屏幕键盘时,视口单位通常不会调整,因为大多数浏览器默认只调整视觉视口的大小。

要选择启用键盘感知布局行为,可以使用 interactive-widget meta 标签:

<meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content">

resizes-content 值告诉浏览器在虚拟键盘打开时调整视口大小。其他值包括 resizes-visual(默认值,仅调整视觉视口大小)和 overlays-content(键盘覆盖而不调整任何大小)。此行为的支持目前主要存在于基于 Chromium 的浏览器中。

Chrome 还通过 VirtualKeyboard API 提供程序化控制。该 API 的跨浏览器支持仍然有限,因此如果你的布局依赖于输入框下方的可见空间,请在真实设备上仔细测试。

浏览器支持

svhlvhdvh 在以下浏览器中受支持:

  • Chrome 108+
  • Safari 15.4+
  • Firefox 101+

现代浏览器的覆盖率很好。上面显示的 height: 100vh 降级模式可以优雅地处理旧版环境。你还可以在 MDN 的视口单位文档上验证当前兼容性。

选择正确的单位

  • 内容必须在初始加载时适配?svh
  • 沉浸式全屏布局?lvh
  • 响应浏览器 UI 的自适应布局?dvh
  • 支持旧版浏览器? → 始终包含 vh 降级方案

结论

CSS 中的移动视口高度问题现在有了一个简洁的原生解决方案。在大多数情况下将 vh 替换为 dvh,当首屏可见性重要时使用 svh,为全屏体验保留 lvh。始终将新单位与 vh 降级方案配对以实现向后兼容,并记住虚拟键盘需要单独处理。无需 JavaScript。

常见问题

可以,但要谨慎。因为 dvh 会随着浏览器 UI 展开或收起而变化,使用 dvh 调整大小的固定元素可能在滚动期间明显移动。对于具有固定高度的粘性标题或页脚,使用静态像素或 rem 值通常更可预测。将 dvh 保留用于全高度容器,而不是小的固定尺寸元素。

对于大多数布局,性能影响可以忽略不计。但是,如果你将 dvh 应用于许多深度嵌套的元素,这些元素会触发复杂的布局重新计算,你可能会在低端设备上看到轻微的卡顿。如果性能至关重要,请在真实硬件上进行性能分析。

它们遵循相同的逻辑,但应用于视口宽度。在大多数移动浏览器上,当地址栏出现或消失时宽度不会改变,因此 svw、lvw 和 dvw 通常返回相同的值。这种区别在侧边面板或其他 UI 元素影响可用宽度的设备或浏览器上更重要。

在 viewport meta 标签中添加 interactive-widget=resizes-content。这告诉浏览器在键盘打开时缩小布局视口,这意味着 dvh 将反映减少的空间。如果没有此设置,键盘会覆盖内容,视口单位保持不变。在真实设备上测试,因为不同浏览器和平台的行为各不相同。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay