它们都是为了解决同一个问题:将组件的子节点渲染到存在于父组件 DOM 层次结构之外的 DOM 节点中。
核心概念对比
特性 | React | Vue 3 |
---|---|---|
功能 | 将子节点渲染到父组件 DOM 结构之外 | 将其包裹的内容移动到应用 DOM 结构中的指定位置 |
API | ReactDOM.createPortal(children, domNode) |
<Teleport> 组件 + to 属性 |
代码位置 | 通常在 render() 方法或组件返回值中使用 |
作为模板中的标签使用 |
举例:
公共 HTML 结构 (index.html)
html
<body>
<div id="root"></div> <!-- React 应用挂载点 -->
<div id="app"></div> <!-- Vue 应用挂载点 -->
<!-- 模态框将要被渲染到这个节点 -->
<div id="modal-root"></div>
</body>
1. React 实现:使用 Portals
jsx
// Modal.jsx
import React, { PureComponent } from 'react'
import { createPortal } from "react-dom"
export class Modal extends PureComponent {
render() {
return createPortal(this.props.children, document.getElementById('modal-root') ))
}
}
jsx
// App.jsx
import React, { PureComponent } from 'react'
import { createPortal } from "react-dom"
import Modal from './Modal'
export class App extends PureComponent {
render() {
return (
<div className='app'>
<h1>App H1</h1>
{
createPortal(<h2>App H2</h2>, document.querySelector("#why"))
}
{/* 2.Modal组件 */}
<Modal>
<h2>我是标题</h2>
<p>我是内容, 哈哈哈</p>
</Modal>
</div>
)
}
}
export default App
2. Vue 3 实现:使用 <Teleport>
vue
<!-- Modal.vue -->
<template>
<Teleport to="#modal-root" v-if="isOpen">
<div class="modal-overlay" @click="$emit('close')">
<div class="modal-content" @click.stop>
<button @click="$emit('close')">关闭</button>
<slot></slot>
</div>
</div>
</Teleport>
</template>
<script setup>
defineProps(['isOpen']);
defineEmits(['close']);
</script>
vue
<!-- App.vue -->
<template>
<div class="app">
<h1>我的 Vue 应用</h1>
<button @click="isModalOpen = true">打开模态框</button>
<!-- 即使 Modal 在这里被使用,它的 HTML 将被 Teleport 到 #modal-root -->
<Modal :is-open="isModalOpen" @close="isModalOpen = false">
<p>这是从 Vue Teleport 渲染的内容!</p>
</Modal>
</div>
</template>
<script setup>
import { ref } from 'vue';
import Modal from './components/Modal.vue';
const isModalOpen = ref(false);
</script>
关键区别与共同点
共同点:
- 逻辑位置不变 :在 React 的虚拟 DOM 树或 Vue 的组件树中,
Modal
组件仍然位于App
组件的子节点位置。这意味着它仍然能继承父组件的上下文(React Context / Vue Provide/Inject),事件冒泡在虚拟树中也会正常发生。 - DOM 位置改变 :最终生成的 HTML 结构被移动到了指定的目标容器(如
#modal-root
)中。
区别:
方面 | React Portal |
Vue 3 <Teleport> |
---|---|---|
语法 | 是一个函数 ReactDOM.createPortal() |
是一个内置组件 <Teleport> |
条件渲染 | 通常在 Portal 外部使用 if (!isOpen) return null; |
可以在 <Teleport> 标签上使用 v-if 或 v-show |
目标指定 | 通过函数第二个参数(DOM 节点)指定 | 通过 to 属性(CSS 选择器字符串)指定 |
组合性 | 更偏向函数式,可在逻辑中动态创建 | 更偏向声明式,作为模板的一部分 |
总结
React Portals 等于 Vue 3 的 <Teleport>
组件。
它们都是用于"传送"DOM结构的解决方案。虽然语法和实现方式不同(React 是函数调用,Vue 是组件声明),但它们解决了完全相同的问题,并且设计理念也高度一致。如果你熟悉其中一个,理解另一个将毫无障碍。