Teleport是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。 --- vue3官方文档
简单案例
下面看个简单的案例:
场景描述: 有个抽屉组件提供修改订单信息功能,但是不同账号的商家修改订单信息的具体功能差异较大,例如分为全球购和普通pop商家,为此你需要设计两个组件,分别给全球购商家和普通pop商家使用。两个组件的基本框架是一致的,包括抽屉title和抽屉footer。
基本设计: 使用原子组件drawer,对外使用defineExpose暴露一个openDrawer的方法,在组件内部判断商家类型,动态加载具体的组件;
思考点: 两个子组件都需要 确认修改和取消按钮,并且位置都在抽屉的footer,如果讲 在抽屉的footer里面再次进行商家账号判断,但是又涉及到需要确认修改的数据传递。能不能将操作逻辑聚合到子组件中,但是它的渲染位置又在父组件的footer???
具体实现: 我们可以在父组件中设置个占位,在子组件中使用Teleport 传送过来,并且没有任何的副作用,在官方文档中已说明
Teleport只改变了渲染的 DOM 结构,它不会影响组件间的逻辑关系。也就是说,如果Teleport包含了一个组件,那么该组件始终和这个使用了Teleport的组件保持逻辑上的父子关系。传入的 props 和触发的事件也会照常工作。
这也意味着来自父组件的注入也会按预期工作,子组件将在 Vue Devtools 中嵌套在父级组件下面,而不是放在实际内容移动到的地方。
父组件
<script setup lang="ts">
import { ref, defineExpose, computed, defineAsyncComponent, provide } from 'vue'
const sonA = defineAsyncComponent(() => import('./components/xxx/index.vue'))
const sonB = defineAsyncComponent(() => import('./components/xxx/index.vue'))
// 抽屉是否显示
const drawerVisible = ref(false)
// 当前渲染的子组件
const curComp = computed(() => {
return isGlobalVender.value ? sonA : sonB
})
const closeDrawer = () => {
drawerVisible.value = false
}
const open = () => {
drawerVisible.value = true
}
defineExpose({
open,
})
provide(MODIFY_WAYBILL_NO_KEY, {
closeDrawer,
})
</script>
<template>
<el-drawer
v-model="drawerVisible"
title="修改订单信息"
>
<component :is="curComp"></component>
<template #footer>
<!-- 此处提供给具体实现修改快递单号组件中的teleport占位使用 -->
<div id="xxxxx-drawer-footer"></div>
</template>
</el-drawer>
</template>
子组件
<template>
// .......
<teleport to="#xxxxx-drawer-footer">
<div>
<rc-button type="primary" @click="handleSubmit">确定修改</rc-button>
<rc-button plain @click="handleCancel">取消</rc-button>
</div>
</teleport>
</template>
Teleport的api
(与官网一致,这里贴出来方便一起阅读)
- props
interface TeleportProps {
/**
* 必填项。指定目标容器。
* 可以是选择器或实际元素。
*/
to: string | HTMLElement
/**
* 当值为 `true` 时,内容将保留在其原始位置
* 而不是移动到目标容器中。
* 可以动态更改。
*/
disabled?: boolean
}
- 示例
<Teleport to="#some-id" />
<Teleport to=".some-class" />
<Teleport to="[data-teleport]" />
有条件地禁用:
<Teleport to="#popup" :disabled="displayVideoInline">
<video src="./my-movie.mp4">
</Teleport>