能,但必须加@supports检测和100vh回退,且优先用min-height: 100dvh而非height,防止内容超长截断;不支持的浏览器会忽略100dvh导致高度塌陷。

100dvh 能不能直接替换 100vh?
能,但必须加 @supports 检测和 100vh 回退。不加检测的话,iOS 15 及更早版本、旧版 Samsung Internet、部分 WebView 会直接忽略该声明,导致元素高度塌陷或消失。
正确写法是用 min-height 而非 height,防止内容超长时被截断:
section {
min-height: 100vh;
}
@supports (min-height: 100dvh) {
section {
min-height: 100dvh;
}
}-
100dvh响应地址栏收放,但某些 Android 浏览器在地址栏完全展开时返回值反而比100svh还小,可能造成意外裁剪 - 别在同一个容器上混用
100dvh和100svh,设备间行为差异会导致 fixed 元素错位 - 全屏弹层、视频封面等强视觉场景才推荐用
100dvh;普通固定导航、底部按钮优先选100svh
fixed 元素贴底时 env(safe-area-inset-bottom) 怎么加才生效?
它不是“自动适配”,而是必须参与计算,且只对文档流内元素的 padding/margin/height 生效。fixed 元素脱离文档流,所以不能靠它撑高容器,只能用来微调位置或留白。
典型错误:给 body 加 padding-bottom: env(safe-area-inset-bottom),但 fixed 子元素不受影响;或写成 height: calc(100vh - env(safe-area-inset-bottom)) 却忘了 env() 返回的是带单位的 px 值,不能直接和无单位的 100vh 运算。
立即学习“前端免费学习笔记(深入)”;
正确做法(以底部操作栏为例):
.bottom-bar {
position: fixed;
bottom: 0;
width: 100%;
padding-bottom: env(safe-area-inset-bottom);
height: calc(60px + env(safe-area-inset-bottom));
}- 必须确保父容器(如
html或body)有明确高度,否则env()计算无意义 -
env(safe-area-inset-bottom)在无安全区设备(如大部分安卓机)上返回0px,无需额外判断 - 如果同时要兼容 iOS 15 及更早版本,纯 CSS 无法可靠处理,得引入 JS 监听
resize并读取window.innerHeight
为什么监听 resize + --vh 变量仍是必要兜底?
因为 100dvh 和 100svh 在 Safari 16.4+、Chrome 109+ 才稳定支持,而当前仍有相当比例用户停留在 iOS 15.x(Safari 不支持这些新单位),且部分企业内嵌 WebView 完全不识别 dvh/svh。
JS 方案核心就两行:
function setVh() {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
window.addEventListener('resize', setVh);
setVh();- 必须监听
resize和orientationchange,部分 Android 浏览器横竖屏切换不触发resize - CSS 中要用
calc(var(--vh, 1vh) * 100),1vh是 fallback,不能省略 - 某些 WebView 不触发
resize,需加定时器轮询innerHeight,但这是下策,仅作保底
fixed 错位不一定是 vh 问题,先查 viewport 和祖先元素
很多所谓 “100vh bug” 其实是 fixed 渲染上下文被污染了。iOS Safari 遇到祖先元素有 overflow: hidden、transform(包括 translateZ(0))、contain: paint 等属性时,会把该祖先当作新的 containing block,导致 fixed 元素相对它定位,而非视口。
排查顺序建议:
- 确认
<meta name="viewport">含width=device-width, initial-scale=1.0, maximum-scale=1.0,缺一不可 - 检查 DOM 树中所有父级是否误加了
overflow或任意transform - 临时加
outline: 1px solid red给疑似祖先,看fixed元素是否随其移动 - 避免用
100vw做 fixed 宽度——它包含滚动条宽度,在 iOS Safari 下右边会溢出
真正难搞的是混合场景:viewport 正确、祖先干净、100dvh 也生效,但滚动后仍错位。这时候大概率是 WebKit 的渲染管线在地址栏动态变化时没及时同步 fixed 层级,只能靠 transform: translateZ(0) 强制重绘,或把 fixed 元素挂到 直接子级。


















