真正稳定的全屏首屏需综合 viewport 控制、CSS 重置、JS 动态适配及多事件监听,纯 height: 100vh 不可靠。

全屏首屏效果(即“一屏填满视口,不滚动就能看到全部内容”)不是靠“全屏 API”实现的,而是用 CSS 视窗单位 + 正确的 viewport 控制达成的。只要 viewport 写错、或父容器有意外 padding/margin、或元素被 flex/grid 以外的布局干扰,100vh 就会失效——这是最常踩的坑。
viewport 必须写对且放在 最前面
很多页面在手机上首屏留白、文字缩成一团、左右可拖动,根本原因就是 viewport 没生效:
-
必须出现在的最顶部,不能被 JS 动态插入,也不能被框架(如 Vue SSR)漏掉 - 写了两次?后一个会覆盖前一个,但首次渲染已按错误配置执行
- 别用
width=375或width=1200这类固定值——折叠屏、iPad 分屏、横屏键盘弹出时全崩 - 禁用缩放(
user-scalable=no)既不真正生效,又违反 WCAG 可访问性要求,直接删掉
height: 100vh 不等于“真全屏”,得防 iOS 键盘和 Safari 缩放干扰
100vh 在 iOS Safari 中有个经典 bug:键盘弹出时,视口高度被压缩,但 100vh 仍按原始高度计算,导致内容被顶出屏幕、按钮点不到。
- 临时方案:监听
focus事件,在输入框获得焦点时,给 body 加 class(如keyboard-open),CSS 中用.keyboard-open .hero { height: 90vh; }回退 - 更稳做法:改用 JS 动态计算
document.documentElement.clientHeight设置高度,再配合resize和orientationchange监听 - 某些安卓 WebView 不触发
resize,得加matchMedia('(min-width: 0px)')做兜底监听
HTML 结构必须“无干扰”,避免隐式 margin/padding 破坏高度
哪怕你写了 height: 100vh,如果父容器有默认 margin、body 有默认 padding、或用了 display: table 类布局,首屏就会塌陷或溢出。
- 重置基础样式:
html, body { margin: 0; padding: 0; height: 100%; },缺一不可 - 确保目标容器是
body直接子元素,或至少没有中间层设置了overflow: hidden/transform/position: relative干扰尺寸继承 - 别用
min-height: 100vh当首屏——它只保证“至少”那么高,但可能更高,破坏“一屏即止”的预期 - 如果用了 Flexbox 布局,确认容器设了
display: flex且子项没写flex-shrink: 1导致被压缩
移动端方向变化时,vh 不自动更新,需手动响应
平板横屏、折叠屏展开、甚至 iOS 横屏键盘弹出,都会让 vh 值失准。仅靠 CSS 无法感知这些变化。
- 必须监听
orientationchange事件(iOS/Safari 支持)和resize(多数安卓支持) - 更通用的做法是用
window.matchMedia监听宽高比变化:matchMedia('(orientation: landscape)')或matchMedia('(min-height: 600px)') - 别在
resize回调里直接操作 DOM 高度——高频触发会导致卡顿,建议节流或用requestAnimationFrame
真正稳定的全屏首屏,从来不是靠一行 height: 100vh 解决的;它是一连串防御性控制:viewport 位置、基础样式重置、JS 动态兜底、以及对各种“非标准视口行为”的显式响应。尤其当页面要兼容 iPad Pro 分屏、安卓折叠屏、iOS 键盘弹出这三类场景时,纯 CSS 方案大概率失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











