理解 CSS 中的动态视口单位
如果你曾经在移动端构建全屏布局,并注意到内容被浏览器地址栏遮挡,那么你就遇到了经典的 100vh 问题。过去的解决方案需要使用 JavaScript 技巧。现在,CSS 通过现代视口单位原生支持这一功能:svh、lvh 和 dvh。
核心要点
- 传统的
vh单位在移动端映射到大视口,当浏览器地址栏可见时会导致内容溢出。 - CSS 现在定义了三种视口状态——大视口(
lvh)、小视口(svh)和动态视口(dvh)——每种都有自己的单位集。 - 对首屏内容使用
svh,对沉浸式全屏布局使用lvh,对响应浏览器 UI 变化的自适应设计使用dvh。 - 动态视口单位不考虑虚拟键盘;在真实设备上测试输入密集型布局。
- 始终为不支持新单位的旧版浏览器提供
vh降级方案。
为什么 vh 在移动端会失效
在桌面端,vh 工作可靠。在移动端,浏览器的 UI——地址栏、标签栏、导航控件——会随着用户滚动而展开和收起。视口高度会变化,但 vh 不会跟随它。
vh 反映的是大视口(类似于 lvh),它假设浏览器界面完全收起。因此,当地址栏可见时,100vh 元素可能会溢出可见区域。内容被截断,按钮无法触及,布局被破坏。
三种视口状态:大视口、小视口和动态视口
CSS Values and Units 规范定义了三种不同的视口状态来解决这个问题:
- 大视口 — 所有浏览器 UI 收起时测量(最大可用高度)
- 小视口 — 所有浏览器 UI 展开时测量(最小可用高度)
- 动态视口 — 跟踪当前活动的状态
每种状态都有对应的 CSS 单位集:
| 视口状态 | 高度单位 | 宽度单位 |
|---|---|---|
| 大视口 | lvh | lvw |
| 小视口 | svh | svw |
| 动态视口 | dvh | dvw |
每个系列还包括 min、max、inline 和 block 变体(dvi、dvb、svmin、lvmax 等)。
重要提示: 在现代浏览器中,vh 实际上映射到 lvh。它们都反映大视口尺寸。
在没有动态 UI 的桌面浏览器上,所有三种视口尺寸都是相同的。
何时使用 svh、lvh 和 dvh
使用 svh 当内容必须在页面初始加载时完全可见,在任何滚动发生之前。它保证你的布局适合最小可能的视口——浏览器界面完全可见。
/* 内容在加载时始终可见,无需滚动 */
.above-the-fold {
min-height: 100svh;
}
使用 lvh 用于沉浸式全屏体验——游戏、启动画面或应用外壳——你希望填充最大可用空间。
/* 浏览器界面隐藏时填充屏幕 */
.fullscreen-app {
height: 100lvh;
}
使用 dvh 当你希望布局随着浏览器 UI 变化而自适应。这是大多数响应式布局最实用的选择。
/* 在移动端有问题 */
.hero { height: 100vh; }
/* 适应浏览器界面变化 */
.hero {
height: 100vh; /* 旧版浏览器的降级方案 */
height: 100dvh; /* 现代浏览器使用这个 */
}
上述降级模式有效,因为不识别 dvh 的浏览器会忽略第二个声明并应用第一个。现代浏览器应用最后一个有效声明,因此 100dvh 生效。
Discover how at OpenReplay.com.
虚拟键盘的注意事项
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 的跨浏览器支持仍然有限,因此如果你的布局依赖于输入框下方的可见空间,请在真实设备上仔细测试。
浏览器支持
svh、lvh 和 dvh 在以下浏览器中受支持:
- 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.