真机上 body { overflow: hidden } 无效是因 iOS Safari 和旧版安卓 WebView 不遵守该声明,需用 position: fixed + 记录/恢复 scrollY 实现锁屏,并在组件卸载时清理样式。

为什么只设 body { overflow: hidden } 在真机上没用
iOS Safari 和部分安卓 WebView(如旧版 UC)压根不把 body 的 overflow: hidden 当回事:视觉上没滚动条,手指一滑,底层页面照样惯性滚动。这不是你 CSS 写错了,是浏览器机制决定的——touchmove 默认行为不会被这个声明拦截。你看到模拟器里“好了”,是因为 Chrome 模拟器不复现这个底层行为。
用 position: fixed 锁定 body 的完整操作
核心不是“禁滚动”,而是让 body 物理上失去滚动能力:固定定位后它脱离文档流,自然无法被拖拽滚动。但直接加 position: fixed 会导致页面跳顶,必须同步处理滚动位置。
- 打开弹窗前,先记录当前滚动偏移:
const scrollY = window.pageYOffset || document.documentElement.scrollTop - 设置
document.body.style.position = 'fixed',再设document.body.style.top = `-${scrollY}px`,把页面“拉回”原位 - 为防宽度塌陷,顺手加上
document.body.style.width = '100%' - 关闭弹窗时,先清空样式:
document.body.style.position = ''、document.body.style.top = ''、document.body.style.width = '' - 最后调用
window.scrollTo(0, scrollY)恢复位置——注意不能只改scrollTop,iOS 下document.body.scrollTop基本恒为 0
Vue/React 中容易漏掉的清理动作
在组件生命周期里直接改 body 样式,风险极高:Modal 多次打开可能样式叠加;关闭后若没还原,后续页面全被锁死;SSR 渲染时 document 不存在还会报错。
- 务必在
onMounted/useEffect的 cleanup 函数中还原样式,不能只依赖“点击关闭按钮”这一路径 - 所有 DOM 操作必须包裹在
if (typeof window !== 'undefined')中 - 如果项目有多个全局 Modal(比如
<appmodal></appmodal>),建议由根布局统一管理锁屏状态,避免竞态冲突
overscroll-behavior 是更轻量的替代方案吗
是,但有前提:overscroll-behavior: none 必须设在弹窗内部**真正可滚动的容器**上(比如 .modal-content),而不是弹窗 wrapper 本身。它不重置滚动位置、不触发重排,体验更干净,但 iOS 16.4 以下不支持,需 fallback 到 position: fixed 方案。
真正难的不是加一行 position: fixed,而是确保每次打开都记录对位置、每次关闭都还原得准,且在框架生命周期里不漏掉任何一次清理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











