100vh/100vw 在 ios 8.3 及更早、android 4.4 及更早中根本不可用,因 webview 存在底层缺陷导致高度计算错误、不响应滚动和软键盘;应改用 rem + js 动态设 font-size,并严格在首次加载、resize、orientationchange 时防抖更新,且需缓存初始 window.innerheight 避免地址栏侵入干扰。

100vh、100vw 在 iOS 8.3 及更早、Android 4.4 及更早中不可靠,不是“兼容性差”,是根本不能用。
为什么直接写 height: 100vh 在旧安卓/iOS 上会出问题
这些系统 WebView(如 Android 4.4 的 Chromium 30、iOS 8.3 Safari)对 viewport 单位的实现存在底层缺陷:
-
100vh常被计算为布局视口高度的一半甚至更小,导致内容被截断 - 页面滚动时,
vh值不更新,固定定位元素飘在错误位置 - 软键盘弹出后,
vh完全不重算,按钮直接消失在可视区外 -
@supports (height: 100vh)在这些环境里本身就不支持或返回false,兜底无效
用 rem + JS 动态设 font-size 替代 vh 的实操要点
核心不是“模拟 vh”,而是把 window.innerHeight 映射为可线性缩放的基准单位:
- 首次加载、
resize、orientationchange三个时机必须都触发setRem(),缺一不可 - 必须加防抖(例如
debounce(fn, 100)),否则 Android 4.4 WebView 中连续 resize 会让font-size疯狂跳变 - 别用
scroll监听来更新——旧机卡顿,且 iOS 8 下window.innerHeight滚动时本身就会抖动 - 避免直接用
window.innerHeight计算:地址栏/状态栏侵入会导致该值动态变化;稳妥做法是取首次加载时的值并缓存
示例逻辑:
function setRem() {
const baseHeight = 640;
const height = window.innerHeight || document.documentElement.clientHeight;
document.documentElement.style.fontSize = (height / baseHeight) + 'px';
}
setRem();
window.addEventListener('resize', debounce(setRem, 100));
window.addEventListener('orientationchange', debounce(setRem, 100));
现代方案(100dvh/100svh)仍需 JS fallback
100dvh 虽能响应键盘弹出、分屏等变化,但 Safari 16.4 之前、Chrome 100 之前均不支持;100svh 在 Chrome 109+ 才稳定。实际落地必须:
- 用
@supports (height: 100dvh)包裹,不能省略 - 降级写法推荐:
height: 100vh; min-height: 100dvh;,而非仅靠100dvh - 若用 CSS 自定义属性做 fallback(如
:root { --vh: 100vh; }),记得后续用@supports覆盖,且要明白它不会响应键盘收起等动态变化——只管初始渲染 - 旧版 Safari 必须监听
resize并手动更新--vh变量,且同样要防抖,否则键盘收起时可能连发 4–5 次
真正麻烦的不是写法本身,而是旧环境里 window.innerHeight 的不可预测性——它既随地址栏进出而变,又在某些 WebView 中无法稳定读取。所有 JS 方案都要先解决这个“源头抖动”,否则再精细的 rem 换算也白搭。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











