React Portals 的冒泡事件不受 DOM 层级影响
FreeGuideOnline
最新
2026-07-06
jsx import { createPortal } from 'react-dom';
function Modal({ children }) { return createPortal(
{children}
,
document.getElementById('modal-root')
);
}
## 事件冒泡在 React 中是如何工作的
React 中的事件系统是合成的(SyntheticEvent),所有事件处理器都会被绑定到根节点上,并通过事件委托统一管理。但 React 保证事件处理器的调用顺序仍然符合**组件树**的层级结构,而不是真实的 DOM 结构。
当你在一个组件内部触发事件时,事件会沿着组件树向上冒泡,依次调用所有祖先组件上注册的同类型事件处理函数,就像普通 DOM 事件冒泡一样。
## Portals 中的冒泡行为:不受 DOM 层级影响
关键点:**Portal 内部触发的事件会冒泡到 React 组件树中的祖先组件,而不是 DOM 树中的祖先节点。**
也就是说,即使 Portal 渲染的 DOM 节点被放置在完全不同的 DOM 分支下,它的冒泡依然遵循 React 组件的嵌套关系。
### 示例场景
假设组件结构如下:
```jsx
function App() {
return (
<div onClick={() => console.log('App clicked')}>
<Parent />
</div>
);
}
function Parent() {
return (
<div onClick={() => console.log('Parent clicked')}>
<Modal>
<button>点击我</button>
</Modal>
</div>
);
}
function Modal({ children }) {
return createPortal(
<div className="modal">{children}</div>,
document.getElementById('modal-root')
);
}
App渲染一个div,内部包含Parent。Parent内部使用了Modal,而Modal将内容传送到#modal-root容器中。- 真实 DOM 结构:
#modal-root位于App的div之外,甚至可能在<body>直接子级。
现在点击 <button>,控制台会输出什么?
Parent clicked
App clicked