React Portal 亮了🔥

2025-06-10

React Portal 亮了🔥

最近我接触到了 Reacts 的 createPortal API,它简直太棒了。

让我分享一下我的经验吧!

作为一名专业的Ember 开发者,我对 React 的热爱从未消退。React 以其面向组件的架构提升了生产力,确保了代码的稳定性,并拥有强大的社区支持。

我不想让您(可能很多 Web 开发人员)每天都会听到这类事情,让您感到厌烦。

我认为,是时候让我们亲手研究一下Portal 了🔥

“门户提供了一种一流的方法,将子项渲染到存在于父组件 DOM 层次结构之外的 DOM 节点中”

一般来说,世界上并不是每个人都能一眼看懂官方文档的定义!至少我不行!(开个玩笑,Reacts 关于 Portal 的文档对初学者更友好,去看看吧

所以我决定采取一种实用的方法:

正如定义中所述,Portals 提供了一种在 DOM 中的其他位置(不在同一层次结构中)渲染反应组件的子组件的方法!

当我意识到这一点时,我心中只剩下疑问。

天哪,事件冒泡怎么办?还有很多……

作为一名专业的 Ember 开发人员,我使用过Ember Wormhole,它是一个插件,可能可以完成与 Ember 中的 Portals 类似的工作。

我继续深入研究 Portal。其中一件事就是它在模态对话框中的用例。

我使用 bootstrap 构建了一个模态组件(覆盖了一些 bootstrap 样式),类似于这个👇

//Modal.js
import React from "react";
import ReactDOM from "react-dom";
export default class Modal extends React.Component {
  onClose = e => {
    this.props.onClose && this.props.onClose(e);
  };
  render() {
    let modal = (<div
      class="modal fade"
      id="exampleModalCenter"
    >
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLongTitle">
              Modal title
            </h5>
            <button
              type="button"
              class="close"
            >
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">...</div>
          <div class="modal-footer">
            <button
              type="button"
              class="btn btn-secondary"
            >
              Close
            </button>
            <button type="button" class="btn btn-primary">
              Save changes
            </button>
          </div>
        </div>
      </div>
    </div>
    );
    return (modal);
  }
}
Enter fullscreen mode Exit fullscreen mode

我把它渲染为 App 👇 的一个子类

//App.js    
import React from "react";
import Modal from "./Modal-Compo";

export default class App extends React.Component {
  onClose = e => {
    this.props.onClose && this.props.onClose(e);
  };

  render() {
    let alignCenter = {
      display: "flex",
      alignItems: "center",
      justifyCenter: "center",
      height: "200px",
      overflow: "hidden",
      width: "50%",
      margin: "auto",
      marginTop: "10%"
    };
    return (
    <div style={alignCenter}>
      <p style={{ height: "100%", margin: "0" }}>
        //some random 100 lines
      </p>
      <Modal onClose={this.onClose}/>
    </div>
  )
 }
}


Enter fullscreen mode Exit fullscreen mode

Atlast 在根元素中渲染了 App 组件 👇

//Index.js
import React from "react";
import ReactDOM from "react-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "jquery/dist/jquery.min.js";
import "bootstrap/dist/js/bootstrap.min.js";
import App from "./components/App";
import "./styles.css";

function WhatAreModals() {
  return (
    <div style={{ height: "100vh" }} className="App">
      <App />
      <button
        type="button"
        className="btn btn-primary"
        data-toggle="modal"
        data-target="#exampleModalCenter"
      >
        Launch demo modal
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<WhatAreModals />, rootElement);

Enter fullscreen mode Exit fullscreen mode

我的原型终于准备好了😄

当我点击启动演示模式 CTA 时,发生了这种情况(哎呀)😕

罪魁祸首是样式为'overflow:hidden' 的App 组件,因为在我们的例子中,Modal 组件被渲染为 App 的子组件,而 App 的 overflow 是隐藏的,所以我们的 Modal 永远不会出现😩

这就是救生门户发挥作用的地方🔥

我刚刚对我的 Modal 组件和 index.html 做了一些调整(为要呈现的 Modal 创建了另一个根元素)

//index.html
<div id="root"></div>
<div id="modal-root"></div>
Enter fullscreen mode Exit fullscreen mode

在 Portal 中渲染 Modal,通过实现createPortal更改返回语句

//Modal.js
ReactDOM.createPortal(modal, document.getElementById("modal-root"));
Enter fullscreen mode Exit fullscreen mode

它运行得很顺畅,

通过将 Modal 组件从容器和层次结构中分离出来,问题得到了解决。

但突然间我陷入了困惑,因为层次结构被破坏了,我怀疑是否会发生事件冒泡?(我想,很多人会质疑这一点!)。

我继续深入挖掘😁

原生 DOM 快照:

反应DOM:

最后,看到这个我很满意,可能很多人都会满意😌
从快照中,我们知道 Reacts 的虚拟 DOM 中的层次结构没有改变,因此事件冒泡会很容易发生。

当父组件具有overflow: hiddenz-index 样式,但需要子组件在视觉上“突破”其容器时,可以广泛使用 Portal。例如,对话框、悬停卡片和工具提示。

我觉得这篇文章会让您对 Reacts 的 createPortal API 感到满意,如果是的话,请随时与您的同事 Web 开发人员分享。

鏂囩珷鏉ユ簮锛�https://dev.to/theaswathprabhu/portals-are-lit-31l8
PREV
平衡信心和冒名顶替综合症
NEXT
测试 React Hook 状态变化