100dvh是目前最轻量有效的css解法,但需配合min-h-[100dvh]、flex布局、移除height=device-height、禁用fixed定位及正确viewport配置才能生效;仅换单位会导致90%项目错位。

直接用 100dvh 替代 100vh 是目前最轻量有效的 CSS 层面解法,但只换单位就上线,90% 的项目会继续错位。
为什么 100vh 在键盘弹出后完全不可信
iOS Safari 和多数安卓 WebView(如华为、UC)根本不会更新 vh 单位值——它被锁定在页面加载时的视口高度。键盘一弹,可视区域压缩了 300px,但容器仍按原始 667px 渲染,结果就是内容被截断、底部按钮悬空或消失。
100dvh 是动态单位,代表“无键盘状态下的完整视口高度”,Chrome 105+、Safari 16.4+ 已稳定支持。但它不是开箱即用的魔法:
- 必须写成
min-height: 100dvh,不能只写height: 100dvh;否则 flex 子项无法正确分配空间 - Tailwind 用户请用
min-h-[100dvh](Tailwind ≥ 3.3),别再用min-h-screen - 删掉
<meta>中的height=device-height——这个参数会让所有vh/dvh单位失效 -
html或body上的h-full/min-h-full也得删,它们会干扰dvh行为
min-h-[100dvh] 必须配合 flex flex-col 使用
min-h-[100dvh] 只设最小高度,不触发弹性分配。若没配 flex 布局,主内容区照样塌成一条线,底部按钮被压缩变形。
根容器(如 #app 或 body)要这样写:
<div id="app" class="flex flex-col min-h-[100dvh]"> <main class="flex-1 overflow-y-auto"></main><footer class="flex-shrink-0"></footer> </div>
-
main加flex-1(等价于flex: 1 1 0%),确保撑满剩余空间且可独立滚动 -
footer加flex-shrink-0,否则键盘弹出会压扁按钮 - 可加
min-w-0到main内部文字容器,避免文字溢出撑宽容器
为什么 position: fixed 必须放弃
它锚定的是视觉视口(visualViewport),而软键盘弹出会压缩该视口,但浏览器不触发重排,fixed 元素位置就“冻”在旧坐标上。iOS 上尤其明显:按钮直接飘到屏幕中间,甚至被顶出可视区。
替代方案有两类,选哪个取决于语义:
- 如果底部按钮是输入操作的一部分(比如聊天页的「发送」),把它和
input放在同一position: relative容器里,用display: flex对齐 - 如果必须全局固定(如导航栏),改用
position: absolute+focusin/blur动态计算bottom值,且必须满足:
– 根容器设position: relative和min-height: 100vh
– 底部元素加width: 100%
–focusin后用setTimeout(() => {}, 0)延迟读取getBoundingClientRect()
真正难处理的不是怎么写,而是不同平台对 visualViewport、dvh、env(keyboard-inset-bottom) 的支持粒度差异极大,且键盘收起延迟、焦点转移时机、WebView 内核版本都会让同一套 CSS 在不同机型上表现不一致。一个 min-h-[100dvh] 在 iOS 17 上完美,在微信安卓 WebView 里可能完全不生效——这时候 JS 监听和降级逻辑不是补充,而是必选项。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











