最近在用 vue3 寫一個小 ui 庫,其中 dialog 組件的彈出框,涉及到了組件層級,也就是 z-index 的問題,下面我們來代碼演示一下
首先是組件 html 部分的代碼:
<template>
<template v-if="visible">
<!-- 遮罩層,z-index 為 10 -->
<div class="aha-dialog-overlay" @click="onClickOverlay"></div>
<!-- dialog 容器,z-index 為 11 -->
<div class="aha-dialog-wrapper">
<div class="aha-dialog">
<header>
<slot name="title"></slot><span @click="close" class="aha-dialog-close"></span>
</header>
<main>
<slot name="content"></slot>
</main>
<footer>
<Button level="main" @click="ok">OK</Button>
<Button @click="cancel">Cancel</Button>
</footer>
</div>
</div>
</template>
</template>
在父組件中引入 dialog 組件,效果如下:

目前來看是沒什么問題的,那如果父組件中有一個層級高的標簽呢?我在父組件寫了兩個容器,其中一個 z-index 為 1,里面包含了 dialog 組件,另一個 z-index 為 2,如下:
<template>
<div>Dialog 示例</div>
<h1>示例1</h1>
<!-- z-index 為 1 的父容器,里面包含了 dialog 組件-->
<div style="position: relative; z-index: 1">
<Button @click="toggle">toggle</Button>
<Dialog v-model:visible="x" :ok="f1" :cancel="f2">
<template v-slot:title>
<strong>加粗的標題</strong>
</template>
<template v-slot:content><strong>hi</strong></template>
</Dialog>
</div>
<!-- 層級高的標簽,z-index 為 2 -->
<div style="
position: relative;
z-index: 2;
width: 300px;
height: 300px;
background: red;
"></div>
</template>
效果如下:

下面我們觸發 dialog 顯示事件:

可以看到 dialog 被擋在了紅色方塊的下面,因為元素在 html 中的層級是受父元素限制的,雖然我們給 dialog 設置了 z-index 為 11,但在父元素 z-index 為 1 的情況下,就算你給子元素設為 1000,他在頁面中的層級也會比 z-index:2 要小。
vue3 新推出的組件 Teleport 就可以解決這個問題,讓子組件不以父組件為層級參照,直接傳送到 body 或者 其他元素下面
我們現在用 Teleport 標簽把我們 dialog 組件需要提升層級的遮罩層 overlay 和彈出層 dialog 包裹起來:

再看一下效果:

ok,完美解決~
