CSS 层叠上下文 & z-index 实战手册

61 阅读2分钟

参考文章


1. 基础规则:position: fixed 自动创建层叠上下文

position: fixed 自身就会创建新的层叠上下文,不需要再单独设置 z-indexabsolute 则必须显式指定 z-index 才会触发。


2. 用 nextZIndex 替代硬编码

禁止手动写死 z-index: 9999 之类的值,容易造成全局层级冲突。应当使用 Element Plus 提供的 useZIndex

2.1 默认基准值

直接调用 nextZIndex() 时,返回值的基准默认是 2000,并在此基础上全局递增。

2.2 调用方式

onMounted 中初始化一次,不要在模板里直接调用,否则每次渲染都会递增。

import { useZIndex } from 'element-plus'
const { nextZIndex } = useZIndex()
const modalZIndex = ref(0)
onMounted(() => {
  modalZIndex.value = nextZIndex()   // 基于 2000 递增
})

3. 如何自定义全局基准值

如果想把基准值从默认的 2000 改成 3000(或其他值),用 <el-config-provider> 包裹组件树来提供 z-index

<template>
  <el-config-provider :z-index="3000">
    <router-view />
  </el-config-provider>
</template>

这样包裹后,内部所有调用 nextZIndex() 的地方都会基于 3000 开始递增。

⚠️ 注意:不要写成空的 <el-config-provider :z-index="3000" />(没有子元素),这会导致“全局污染”,让其他未包裹的组件也可能意外读到这个值。始终让 Provider 包裹实际需要接收配置的内容。


4. 局部层叠上下文(谨慎使用)

如果不想参与全局 z-index 竞争,可以用 isolation: isolate 创建局部层叠上下文:

.parent {
  isolation: isolate;
}

⚠️ 副作用:内部的所有层叠元素(包括 position: fixed)将被限制在此上下文中,无法与外部全局层叠元素竞争。原本可以覆盖全屏的 fixed 元素可能被外部高层级元素遮挡。

应对策略

  • 先确认内部是否有需要与全局竞争的全屏 fixed 元素,没有的话才用 isolation
  • 如果有,优先使用 Teleport 将需要“跳出”的元素传送到 body,而不是创建局部上下文。

5. 跳出限制:Teleportbody

需要突破父级 overflowtransform 或局部层叠上下文时,用 Teleport 将元素挂载到 body,直接进入根层叠上下文。

  • el-dialog:默认 append-to-body="false",留在原地;需要跳出时手动开启 :append-to-body="true"
  • el-tooltip:默认 teleported="true",已传送到 body。若仍被遮挡,通常是项目中有违规的硬编码 z-index,应从源头清理,而非给 Tooltip 加更大值。

总结:三条原则

  1. 全局层级:用 nextZIndex 自动递增,禁止硬编码;通过 <el-config-provider> 设定基准值,并确保它包裹实际内容。
  2. 局部隔离:首选 Teleport 移出局部上下文,慎用 isolation: isolate
  3. 突破限制:需要物理脱离时,用 Teleport 送到 body