vue3内置组件-Teleport 传送

286 阅读2分钟

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>