在React中,父组件引入子组件后,子组件会直接在父组件内部渲染。换句话说,React元素中的子组件,在DOM中,也会是其父组件对应DOM的后代元素。
但是,在有些场景下如果将子组件直接渲染为父组件的后代,在网页显示时会出现一些问题。比如,需要在React中添加一个会盖住其他元素的Backdrop组件,Backdrop显示后,页面中所有的元素都会被遮盖。很显然这里需要用到定位,但是如果将遮罩层直接在当前组件中渲染的话,遮罩层会成为当前组件的后代元素。如果此时,当前元素后边的兄弟元素中有开启定位的情况出现,且层级不低于当前元素时,便会出现盖住遮罩层的情况。
const Backdrop = () => {
return <div
style={
{
position:'fixed',
top:0,
bottom:0,
left:0,
right:0,
background:'rgba(0,0,0,.3)',
zIndex:9999
}
}
>
</div>
};
const Box = props => {
return <div
style={
{
width:100,
height:100,
background:props.bgColor
}
}
>
{props.children}
</div>
};
const App = () => {
return <div>
<Box bgColor='yellowgreen'>
<Backdrop/>
</Box>
<Box bgColor='orange' />
</div>;
};
ReactDOM.render(
<App/>,
document.getElementById('root')
);
上例代码中,App组件中引入了两个Box组件,一个绿色,一个橙色。绿色组件中引入了Backdrop组件,Backdrop组件是一个遮罩层,可以在覆盖住整个网页。
现在三个组件的关系是,绿色Box是橙色Box的兄弟元素,Backdrop是绿色Box的子元素。如果Box组件没有开启定位,遮罩层可以正常显示覆盖整个页面。
Backdrop能够盖住页面
但是如果为Box开启定位,并设置层级会出现什么情况呢?
const Box = props => {
return <div
style={
{
width:100,
height:100,
background:props.bgColor,
position:'relative',
zIndex:1
}
}
>
{props.children}
</div>
};
现在修改Box组件,开启相对定位,并设置了z-index为1,结果页面变成了这个样子:
和上图对比,显然橙色的box没有被盖住,这是为什么呢?首先我们来看看他们的结构:
<App>
<绿色Box>
<遮罩/>
</绿色Box>
<橙色Box/>
</App>
绿色Box和橙色Box都开启了定位,且z-index相同都为1,但是由于橙色在后边,所以实际层级是高于绿色的。由于绿色是遮罩层的父元素,所以即使遮罩的层级是9999也依然盖不住橙色。
问题出在了哪?遮罩层的作用,是用来盖住其他元素的,它本就不该作为Box的子元素出现,作为子元素了,就难免会出现类似问题。所以我们需要在Box中使用遮罩,但是又不能使他成为Box的子元素。怎么办呢?React为我们提供了一个“传送门”可以将元素传送到指定的位置上。
通过ReactDOM中的createPortal()方法,可以在渲染元素时将元素渲染到网页中的指定位置。这个方法就和他的名字一样,给React元素开启了一个传送门,让它可以去到它应该去的地方。
portal的用法
- 在index.html中添加一个新的元素
- 在组件中中通过ReactDOM.createPortal()将元素渲染到新建的元素中
在index.html中添加新元素:
<div id="backdrop"></div>
修改Backdrop组件:
const backdropDOM = document.getElementById('backdrop');
const Backdrop = () => {
return ReactDOM.createPortal(
<div
style={
{
position:'fixed',
top:0,
bottom:0,
left:0,
right:0,
zIndex:9999,
background:'rgba(0,0,0,.3)'
}
}
>
</div>,
backdropDOM
);
};
如此一来,我们虽然是在Box中引入了Backdrop,但是由于在Backdrop中开启了“传送门”,Backdrop就会直接渲染到网页中id为backdrop的div中,这样一来上边的问题就解决了!嘿嘿,不错吧!
打卡
Portal技术确实不错呀!!超老师超棒的!!
有几个可以忽略不记的书写失误哈 ? :
1- 现在修改Box组件,开启相对定位,并设置了z-index为1,结果页面变成了《整个样子》
—> 这个样子
2- 问题出在了《拿》?–> 出在了哪儿?
3- 但是《有》不能使他《称为》Box的子元素. 怎么办呢? –> 又不能使他成为
?
“嘿嘿,不错吧”,突然撒娇,超哥好可爱
18.2版本,直接引用import { createPortal } from ‘react-dom’;
调用createPortal() 方法即可,不需要使用ReactDom对象