遮罩层必须用 position: fixed 而非 absolute,因 absolute 依赖定位祖先、滚动时偏移,fixed 相对视口保持覆盖;但父容器含 transform 等属性时 fixed 会降级,需挂载于 body 下并注意移动端 safari 渲染延迟、z-index 统一管理、vw/vh 尺寸设定、spinner 居中容错、svg 防压缩、aria-hidden/inert 辅助适配、escape 关闭、opacity 过渡卸载及滚动锚点保持。

遮罩层为什么必须用 position: fixed 而不是 absolute
因为 absolute 依赖最近的定位祖先,页面滚动时遮罩会跟着内容“跑偏”或消失;fixed 直接相对于视口,滚动不移位,才能真正“盖住整个屏幕”。但要注意:如果父容器设置了 transform、perspective 或 filter,fixed 会降级为相对该容器定位——这是最常被忽略的兼容性陷阱。
- 确保遮罩层直接挂载在
下,或至少祖先链上没带transform的元素 - 移动端 Safari 对
fixed+flex的组合有渲染延迟,需加will-change: transform强制硬件加速 - 避免给遮罩层设
z-index: 9999这类魔法数字,统一用 CSS 变量(如--z-loading: 1000)便于全局控制层级
display: flex 居中 spinner 时的常见错觉
看着居中了,实际可能被父容器的 padding、border 或子元素的 margin 拉偏。Flex 居中只保证“主轴/交叉轴对齐”,不自动抵消外部尺寸干扰。
- 遮罩层自身必须设
width: 100vw; height: 100vh;,不能只靠top: 0; left: 0; right: 0; bottom: 0;—— 后者在某些缩放场景下会漏边 - spinner 元素要加
margin: auto;,而不是仅靠justify-content和align-items—— 这能兜底处理子元素尺寸突变(比如动态切换不同大小的 loading 图标) - 如果 spinner 是 SVG,记得加
flex-shrink: 0;,否则在窄屏下可能被压缩变形
如何让遮罩层不影响键盘焦点和屏幕阅读器
全局遮罩一旦出现,用户就不能操作背后内容,但也不能把整个页面“锁死”得让辅助技术失灵。关键在 aria-hidden 和 inert 的配合使用。
- 遮罩层本身设
aria-modal="true"并用role="dialog",告诉屏幕阅读器这是一个模态层 - 遮罩层出现时,给
加inert属性(现代浏览器支持),或手动遍历所有可聚焦元素设tabindex="-1"+aria-hidden="true" - 别忘了监听
Escape键关闭遮罩——即使只是 loading 状态,也要允许用户主动中断,尤其长请求场景下
加载完成时怎么安全移除遮罩而不闪屏
直接 display: none 或 remove() 会造成视觉跳变,尤其当页面有滚动条时:遮罩消失 → 滚动条恢复 → 页面右移,用户感知明显。
- 用
opacity: 0; pointer-events: none;配合 CSS 过渡,比 display 切换更平滑 - 移除前先
getBoundingClientRect()记录当前滚动位置,再恢复时window.scrollTo()回填,避免 Safari 滚动锚点丢失 - 如果遮罩是通过 Vue/React 动态渲染的,确保卸载逻辑在
useEffect或onUnmounted中执行,而非仅靠条件渲染——否则组件销毁后事件监听器残留可能导致重复触发
真正难的不是铺满和居中,而是遮罩存在期间页面状态的“静默接管”:滚动、焦点、缩放、辅助访问、动画中断……每个环节断掉,用户都会觉得“卡了一下”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











