Vue3中新增加了一个内置组件Teleport。官网教程的叙述实在是晦涩难懂,所以我们一起研究一下teleport。这个组件的主要作用是,将模板内的 DOM 元素移动到其他位置。

teleport的使用场景:业务开发的过程中,我们经常会封装一些常用的组件,例如 Modal 组件。相信大家在使用 Modal 组件的过程中,经常会遇到一个问题,那就是 Modal 的定位问题。Teleport提供了一种干净的方法,允许我们控制在 DOM 中哪个父节点下呈现 HTML,而不必求助于全局状态或将其拆分为两个组件。我们只需要将弹窗内容放入Teleport内,并设置to属性为body,表示弹窗组件每次渲染都会做为body的子级,这样之前的问题就能得到解决。

例子1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const app = Vue.createApp({})

app.component('modal-button', {
template: `
<button @click="modalOpen = true">
Open full screen modal!(With teleport!)
</button>

<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">关闭</button>
</div>
</div>
</teleport>
`,
data() {
return {
modalOpen: false
}
}
})

app.mount('#app')

此时点击按钮,触发modalOpen,则会显示模态框,通过设置teleport的to到body,那么实际上这个模态框是作为body元素的子节点来显示的。

例子2:与Vue components一起使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const app = Vue.createApp({
template: `
<h1>Root instance</h1>
<parent-component />
`
})

app.component('parent-component', {
template: `
<h2>This is a parent component</h2>
<teleport to="#endofbody">
<child-component name="John" />
</teleport>
`
})

app.component('child-component', {
props: ['name'],
template: `
<div>Hello, </div>
`
})

此时,就算是在不同的地方渲染的child-component,它仍然将是parent-component的子级,并将从中接收name prop。这也意味着来自父组件的注入按预期工作,并且子组件将嵌套在Vue Devtools中的父组件之下,而不是放在实际内容移动到的位置。

例子3:在同一目标上使用多个teleport

1
2
3
4
5
6
7
8
9
10
11
12
<teleport to="#modals">
<div>A</div>
</teleport>
<teleport to="#modals">
<div>B</div>
</teleport>

<!-- result -->
<div id="modals"> //最后的形成
<div>A</div>
<div>B</div>
</div>

最常见的场景,就是是一个可重用的Modal组件,它可能同时有多个实例处于活动的状态。