React Portals

1 views

React Portals
Loading...

什么是 Portal

React Portal 是一种优秀的方法,可以将子组件渲染到由组件树层次结构定义的父 DOM 层次结构之外的 DOM 节点中。Portal 的最常见用例是子组件需要从视觉上脱离父容器的情况,如下所示:

  • 模态对话框
  • 工具提示
  • 悬浮卡片
  • 加载器

为什么需要 Portal

当我们在特定元素(父组件)中使用模态时,模态的高度和宽度将从模态所在的组件继承。因此,模态可能会被裁剪,而无法在应用程序中正确显示。传统上模态需要 CSS 属性,如 overflow:hiddenz-index ,以避免出现这一问题。

portal 问题

使用 Portal 可以脱离父组件,达到下面的效果👇🏻:

Portal 效果

现在的 React-Portal

在 React16 之后,React 提供了一个函数 ReactDOM.createPortal(child, container) 来渲染一个 React 子元素 到指定的 container 中,使用起来非常方便;

基本用法如下:

import { createPortal } from 'react-dom';
// ...
<div>
<p>这个子节点被放置在父节点 div 中。</p>
{createPortal(<p>这个子节点被放置在 document body 中。</p>, document.body)}
</div>;

Portal 的优势

1. 突破 DOM 结构限制

  • 通常情况下,React 组件会渲染到它们的父组件之内。但是,有些情况下,你可能希望将组件渲染到 DOM 树的某个特定位置,比如模态框、悬浮菜单、工具提示等。这些组件通常需要脱离父组件的层级,直接渲染到 body 或其他根节点下。React Portal 可以让你轻松实现这一点。

2. 避免 CSS 样式干扰

  • 由于组件渲染到 DOM 的其他部分,可以避免父组件的 CSS 样式(如 overflow: hiddenz-index)对这些组件的影响。比如,在父组件内有 overflow 限制的情况下,使用 Portal 渲染的模态框或浮动组件仍然可以显示在页面顶层,不受父组件样式的限制。

3. 提高可访问性和可维护性

  • 将弹窗、对话框等 UI 元素渲染到 body 节点下,可以避免父组件因变更而导致弹窗消失或样式错乱。这种分离也使得这些组件的结构更加简单和清晰,有助于代码的可维护性。

4. 更灵活的事件处理

  • 即使使用 Portal 渲染的组件位于 DOM 树的不同位置,React 的事件冒泡机制依然可以正常工作。也就是说,即便组件在视觉上脱离了其父组件,事件依然可以按照预期在 React 的组件树中进行冒泡和捕获。这种行为对事件处理更加灵活,比如你仍然可以在父组件中处理从 Portal 渲染的子组件发出的事件。

5. 支持复杂的 UI 交互

  • 在创建复杂的 UI 组件时,如模态对话框、浮动菜单等,使用 Portal 可以使你更方便地管理这些组件的层级和渲染位置,确保用户体验的一致性和流畅性。

综上所述,React Portal 为开发者提供了更加灵活的组件渲染方式,特别适用于那些需要脱离当前组件树结构进行渲染的场景。

和直接通过 dom 插入有啥区别吗?

React Portal 和直接通过 DOM 操作插入元素有一些重要的区别,尤其是在 React 应用中,这些区别体现在以下几个方面:

1. 与 React 状态和生命周期的集成

  • React Portal:使用 React Portal 渲染的组件仍然是 React 组件树的一部分,它们完全受 React 管理。这意味着这些组件能够正常地使用 React 的状态管理、生命周期方法以及上下文(context)。这确保了组件的可预测性和一致性。
  • 直接 DOM 操作:如果你直接通过 DOM 操作来插入元素,这些元素就不再受 React 管理。它们不属于 React 的组件树,无法使用 React 的状态和生命周期方法,也无法直接从 React 组件中获取数据或响应状态变化。手动管理这些元素的生命周期和状态会变得复杂,容易引入 bug。

2. 事件处理

  • React Portal:Portal 渲染的组件虽然在 DOM 树中位置不同,但它仍然在 React 的组件树中,因此 React 的事件机制(如事件冒泡和捕获)仍然有效。你可以在父组件中正常处理从 Portal 渲染的子组件发出的事件。
  • 直接 DOM 操作:手动插入的 DOM 元素不在 React 的管理范围内,因此无法直接参与 React 的事件处理机制。如果你需要对这些元素进行事件处理,必须使用原生的 DOM 事件监听,这增加了额外的复杂性,且容易导致事件管理的混乱。

3. 代码可维护性

  • React Portal:使用 Portal 时,所有的组件渲染逻辑仍然在 React 中统一管理,代码结构更加清晰、易于维护。你可以使用 JSX 语法来描述组件结构,并保持一致的编程范式。
  • 直接 DOM 操作:直接操作 DOM 通常需要使用原生 JavaScript 方法(如 document.createElementappendChild),这种方式会打破 React 的单向数据流,导致代码变得混杂、不易维护。而且,直接操作 DOM 可能导致与 React 渲染机制冲突,尤其是在 React 重新渲染时,手动插入的 DOM 可能会被覆盖或移除。

4. 性能

  • React Portal:Portal 是 React 的一种官方特性,设计上已经考虑了性能问题。React 会高效地处理 Portal 的更新和渲染,保证应用性能的优化。
  • 直接 DOM 操作:直接操作 DOM 如果不小心,可能会导致性能问题。例如,如果你频繁地操作 DOM,可能会导致浏览器的回流和重绘,进而影响应用性能。此外,手动管理 DOM 更新也容易导致与 React 的自动化 DOM diffing 冲突,可能导致不必要的性能损耗。

5. 可预测性

  • React Portal:Portal 渲染的元素行为是可预测的,因为它仍然依赖于 React 的渲染机制和生命周期管理。
  • 直接 DOM 操作:直接操作 DOM 会带来不确定性,尤其是在与 React 的渲染机制交互时。React 不知道这些手动操作的存在,可能在下一次渲染时无意中覆盖或修改这些 DOM 元素。

总结:

React Portal 提供了与 React 生态系统的深度集成,而直接操作 DOM 则通常会绕开 React 的管理,这可能会导致代码复杂性增加、维护难度加大以及性能问题。因此,在 React 应用中,使用 React Portal 是更好的实践方式,而不是直接操作 DOM。