iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > 其他 >Vue3之Teleport组件怎么使用
  • 695
分享到

Vue3之Teleport组件怎么使用

Vue3teleport 2023-05-14 22:05:50 695人浏览 安东尼
摘要

Teleport 组件解决的问题版本:3.2.31如果要实现一个 “蒙层” 的功能,并且该 “蒙层” 可以遮挡页面上的所有元素,通常情况下我们会选择直接在 标签下渲染 “蒙层” 内容。如果在vue.js 2 中实现这个功能,只能通过原生 D

Teleport 组件解决的问题

版本:3.2.31

如果要实现一个 “蒙层” 的功能,并且该 “蒙层” 可以遮挡页面上的所有元素,通常情况下我们会选择直接在 标签下渲染 “蒙层” 内容。如果在vue.js 2 中实现这个功能,只能通过原生 DOM api 来手动搬运 DOM元素实现,这就会使得元素的渲染与 Vue.js 的渲染机制脱节,并会导致各种可预见或不可遇见的问题。

Vue.js 3 中内建的 Teleport 组件,可以将指定内容渲染到特定容器中,而不受DOM层级的限制。可以很好的解决这个问题。

下面,我们来看看 Teleport 组件是如何解决这个问题的。如下是基于 Teleport 组件实现的蒙层组件的模板:

<template>
  <Teleport to="body">
    <div class="overlay"></div>
  </Teleport>
</template>
<style scoped>
  .verlay {
    z-index: 9999;
  }
</style>

可以看到,蒙层组件要渲染的内容都包含在 Teleport 组件内,即作为 Teleport 组件的插槽。

通过为 Teleport 组件指定渲染目标 body,即 to 属性的值,该组件就会把它的插槽内容渲染到 body 下,而不会按照模板的 DOM 层级来渲染,于是就实现了跨 DOM 层级的渲染。

从而实现了蒙层可以遮挡页面中的所有内容。

Teleport 组件的基本结构

// packages/runtime-core/src/components/Teleport.ts
export const TeleportImpl = {
  // Teleport 组件独有的特性,用作标识
  __isTeleport: true,
  // 客户端渲染 Teleport 组件
  process() {},
  // 移除 Teleport
  remove() {},
  //  移动 Teleport
  move: moveTeleport,
  // 服务端渲染 Teleport
  hydrate: hydrateTeleport
}
export const Teleport = TeleportImpl as any as {
  __isTeleport: true
  new (): { $props: VnodeProps & TeleportProps }
}

我们对 Teleport 组件的源码做了精简,如上面的代码所示,可以看到,一个组件就是一个选项对象。Teleport 组件上有 __isTeleport、process、remove、move、hydrate 等属性。其中 __isTeleport 属性是 Teleport 组件独有的特性,用作标识。process 函数是渲染 Teleport 组件的主要渲染逻辑,它从渲染器中分离出来,可以避免渲染器逻辑代码 “膨胀”。

Teleport 组件 process 函数

process 函数主要用于在客户端渲染 Teleport 组件。由于 Teleport 组件需要渲染器的底层支持,因此将 Teleport 组件的渲染逻辑从渲染器中分离出来,在 Teleport 组件中实现其渲染逻辑。这么做有以下两点好处:

  • 可以避免渲染器逻辑代码 “膨胀”;

  • 当用户没有使用 Teleport 组件时,由于 Teleport 的渲染逻辑被分离,因此可以利用 Tree-Shaking 机制在最终的 bundle 中删除 Teleport 相关的代码,使得最终构建包的体积变小。

patch 函数中对 process 函数的调用如下:

// packages/runtime-core/src/renderer.ts
const patch: PatchFn = (
    n1,
    n2,
    container,
    anchor = null,
    parentComponent = null,
    parentSuspense = null,
    isSVG = false,
    slotScopeIds = null,
    optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
  ) => {
    // 省略部分代码
    const { type, ref, shapeFlag } = n2
    switch (type) {
      // 省略部分代码
      default:
        // 省略部分代码
        // shapeFlag 的类型为 TELEPORT,则它是 Teleport 组件
        // 调用 Teleport 组件选项中的 process 函数将控制权交接出去
        // 传递给 process 函数的第五个参数是渲染器的一些内部方法
        else if (shapeFlag & ShapeFlags.TELEPORT) {
          ;(type as typeof TeleportImpl).process(
            n1 as TeleportVNode,
            n2 as TeleportVNode,
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized,
            internals
          )
        }
        // 省略部分代码
    }
    // 省略部分代码
  }

从上面的源码中可以看到,我们通过vnode 的 shapeFlag 来判断组件是否是 Teleport 组件。如果是,则直接调用组件选项中定义的 process 函数将渲染控制权完全交接出去,这样就实现了渲染逻辑的分离。

Teleport 组件的挂载

// packages/runtime-core/src/components/Teleport.ts
if (n1 == null) {
  // 首次渲染 Teleport
  // insert anchors in the main view
  // 往 container 中插入 Teleport 的注释
  const placeholder = (n2.el = __DEV__
    ? createComment('teleport start')
    : createText(''))
  const mainAnchor = (n2.anchor = __DEV__
    ? createComment('teleport end')
    : createText(''))
  insert(placeholder, container, anchor)
  insert(mainAnchor, container, anchor)
  // 获取容器,即挂载点
  const target = (n2.target = resolveTarget(n2.props, querySelector))
  const targetAnchor = (n2.targetAnchor = createText(''))
  // 如果挂载点存在,则将
  if (target) {
    insert(targetAnchor, target)
    // #2652 we could be teleporting from a non-SVG tree into an SVG tree
    isSVG = isSVG || isTargetSVG(target)
  } else if (__DEV__ && !disabled) {
    warn('Invalid Teleport target on mount:', target, `(${typeof target})`)
  }
  // 将 n2.children 渲染到指定挂载点
  const mount = (container: RendererElement, anchor: RendererNode) => {
    // Teleport *always* has Array children. This is enforced in both the
    // compiler and vnode children nORMalization.
    if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
      // 调用渲染器内部的 mountChildren 方法渲染 Teleport 组件的插槽内容
      mountChildren(
        children as VNodeArrayChildren,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    }
  }
  // 挂载 Teleport
  if (disabled) {
    // 如果 Teleport 组件的 disabled 为 true,说明禁用了 <teleport> 的功能,Teleport 只会在 container 中渲染
    mount(container, mainAnchor)
  } else if (target) {
    // 如果没有禁用 <teleport> 的功能,并且存在挂载点,则将其插槽内容渲染到target容中
    mount(target, targetAnchor)
  }
}

从上面的源码中可以看到,如果旧的虚拟节点 (n1) 不存在,则执行 Teleport 组件的挂载。然后调用 resolveTarget 函数,根据 props.to 属性的值来取得真正的挂载点。

如果没有禁用 的功能 (disabled 为 false ),则调用渲染器内部的 mountChildren 方法将 Teleport 组件挂载到目标元素中。如果 的功能被禁用,则 Teleport 组件将会在周围父组件中指定了 的位置渲染。

Teleport 组件的更新

Teleport 组件在更新时需要考虑多种情况,如下面的代码所示:

// packages/runtime-core/src/components/Teleport.ts
else {
  // 更新 Teleport 组件
  // update content
  n2.el = n1.el
  const mainAnchor = (n2.anchor = n1.anchor)!
  // 挂载点
  const target = (n2.target = n1.target)!
  // 锚点
  const targetAnchor = (n2.targetAnchor = n1.targetAnchor)!
  // 判断 Teleport 组件是否禁用了 
  const wasDisabled = isTeleportDisabled(n1.props)
  // 如果禁用了 <teleport> 的功能,那么挂载点就是周围父组件,否则就是 to 指定的目标挂载点
  const currentContainer = wasDisabled ? container : target
  const currentAnchor = wasDisabled ? mainAnchor : targetAnchor
  // 目标挂载点是否是 SVG 标签元素
  isSVG = isSVG || isTargetSVG(target)
  // 动态子节点的更新
  if (dynamicChildren) {
    // fast path when the teleport happens to be a block root
    patchBlockChildren(
      n1.dynamicChildren!,
      dynamicChildren,
      currentContainer,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds
    )
    // even in block tree mode we need to make sure all root-level nodes
    // in the teleport inherit previous DOM references so that they can
    // be moved in future patches.
    // 确保所有根级节点在移动之前可以继承之前的 DOM 引用,以便它们在未来的补丁中移动
    traverseStaticChildren(n1, n2, true)
  } else if (!optimized) {
    // 更新子节点
    patchChildren(
      n1,
      n2,
      currentContainer,
      currentAnchor,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      false
    )
  }
  // 如果禁用了 <teleport> 的功能
  if (disabled) {
    if (!wasDisabled) {
      // enabled -> disabled
      // move into main container
      // 将 Teleport 移动到container容器中
      moveTeleport(
        n2,
        container,
        mainAnchor,
        internals,
        TeleportMoveTypes.TOGGLE
      )
    }
  } else {
    // 没有禁用 <teleport> 的功能,判断 to 是否发生变化
    // target changed
    // 如果新旧 to 的值不同,则需要对内容进行移动
    if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) {
      // 获取新的目标容器
      const nextTarget = (n2.target = resolveTarget(
        n2.props,
        querySelector
      ))
      if (nextTarget) {
        // 移动到新的容器中
        moveTeleport(
          n2,
          nextTarget,
          null,
          internals,
          TeleportMoveTypes.TARGET_CHANGE
        )
      } else if (__DEV__) {
        warn(
          'Invalid Teleport target on update:',
          target,
          `(${typeof target})`
        )
      }
    } else if (wasDisabled) {
      // disabled -> enabled
      // move into teleport target
      // 
      moveTeleport(
        n2,
        target,
        targetAnchor,
        internals,
        TeleportMoveTypes.TOGGLE
      )
    }
  }
}

如果 Teleport 组件的子节点中有动态子节点,则调用 patchBlockChildren 函数来更新子节点,否则就调用 patchChildren 函数来更新子节点。

接下来判断 Teleport 的功能是否被禁用。如果被禁用了,即 Teleport 组件的 disabled 属性为 true,此时 Teleport 组件只会在周围父组件中指定了 的位置渲染。

如果没有被禁用,那么需要判断 Teleport 组件的 to 属性值是否发生变化。如果发生变化,则需要获取新的挂载点,然后调用 moveTeleport 函数将Teleport组件挂载到到新的挂载点中。如果没有发生变化,则 Teleport 组件将会挂载到先的挂载点中。

moveTeleport 移动Teleport 组件

// packages/runtime-core/src/components/Teleport.ts
function moveTeleport(
  vnode: VNode,
  container: RendererElement,
  parentAnchor: RendererNode | null,
  { o: { insert }, m: move }: RendererInternals,
  moveType: TeleportMoveTypes = TeleportMoveTypes.REORDER
) {
  // move target anchor if this is a target change.
  // 插入到目标容器中
  if (moveType === TeleportMoveTypes.TARGET_CHANGE) {
    insert(vnode.targetAnchor!, container, parentAnchor)
  }
  const { el, anchor, shapeFlag, children, props } = vnode
  const isReorder = moveType === TeleportMoveTypes.REORDER
  // move main view anchor if this is a re-order.
  if (isReorder) {
    // 插入到目标容器中
    insert(el!, container, parentAnchor)
  }
  // if this is a re-order and teleport is enabled (content is in target)
  // do not move children. So the opposite is: only move children if this
  // is not a reorder, or the teleport is disabled
  if (!isReorder || isTeleportDisabled(props)) {
    // Teleport has either Array children or no children.
    if (shapeFlag &amp; ShapeFlags.ARRAY_CHILDREN) {
      // 遍历子节点
      for (let i = 0; i &lt; (children as VNode[]).length; i++) {
        // 调用 渲染器的黑布方法 move将子节点移动到目标元素中
        move(
          (children as VNode[])[i],
          container,
          parentAnchor,
          MoveType.REORDER
        )
      }
    }
  }
  // move main view anchor if this is a re-order.
  if (isReorder) {
    // 插入到目标容器中
    insert(anchor!, container, parentAnchor)
  }
}

从上面的源码中可以看到,将 Teleport 组件移动到目标挂载点中,实际上就是调用渲染器的内部方法 insert 和 move 来实现子节点的插入和移动。

hydrateTeleport 服务端渲染 Teleport 组件

hydrateTeleport 函数用于在服务器端渲染 Teleport 组件,其源码如下:

// packages/runtime-core/src/components/Teleport.ts
// 服务端渲染 Teleport
function hydrateTeleport(
  node: Node,
  vnode: TeleportVNode,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  slotScopeIds: string[] | null,
  optimized: boolean,
  {
    o: { nextSibling, parentNode, querySelector }
  }: RendererInternals<Node, Element>,
  hydrateChildren: (
    node: Node | null,
    vnode: VNode,
    container: Element,
    parentComponent: ComponentInternalInstance | null,
    parentSuspense: SuspenseBoundary | null,
    slotScopeIds: string[] | null,
    optimized: boolean
  ) => Node | null
): Node | null {
  // 获取挂载点
  const target = (vnode.target = resolveTarget<Element>(
    vnode.props,
    querySelector
  ))
  if (target) {
    // if multiple teleports rendered to the same target element, we need to
    // pick up from where the last teleport finished instead of the first node
    const targetNode =
      (target as TeleportTargetElement)._lpa || target.firstChild
    if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
      // <teleport> 的功能被禁用,将 Teleport 渲染到父组件中指定了 <teleport> 的位置
      if (isTeleportDisabled(vnode.props)) {
        vnode.anchor = hydrateChildren(
          nextSibling(node),
          vnode,
          parentNode(node)!,
          parentComponent,
          parentSuspense,
          slotScopeIds,
          optimized
        )
        vnode.targetAnchor = targetNode
      } else {
        vnode.anchor = nextSibling(node)
        // 将 Teleport 渲染到目标容器中
        vnode.targetAnchor = hydrateChildren(
          targetNode,
          vnode,
          target,
          parentComponent,
          parentSuspense,
          slotScopeIds,
          optimized
        )
      }
      ;(target as TeleportTargetElement)._lpa =
        vnode.targetAnchor && nextSibling(vnode.targetAnchor as Node)
    }
  }
  return vnode.anchor && nextSibling(vnode.anchor as Node)
}

可以看到,在服务端渲染 Teleport 组件时,调用的是服务端渲染的 hydrateChildren 函数来渲染Teleport的内容。如果 的功能被禁用,将 Teleport 渲染到父组件中指定了 的位置,否则将 Teleport 渲染到目标容器target中。

以上就是vue3之Teleport组件怎么使用的详细内容,更多请关注编程网其它相关文章!

--结束END--

本文标题: Vue3之Teleport组件怎么使用

本文链接: https://www.lsjlt.com/news/208303.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue3之Teleport组件怎么使用
    Teleport 组件解决的问题版本:3.2.31如果要实现一个 “蒙层” 的功能,并且该 “蒙层” 可以遮挡页面上的所有元素,通常情况下我们会选择直接在 标签下渲染 “蒙层” 内容。如果在Vue.js 2 中实现这个功能,只能通过原生 D...
    99+
    2023-05-14
    Vue3 teleport
  • Vue3之Teleport组件如何使用
    这篇文章主要介绍了Vue3之Teleport组件如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue3之Teleport组件如何使用文章都会有所收获,下面我们一起来看看吧。Teleport 组件解决的问...
    99+
    2023-07-06
  • Vue3内置组件Teleport使用方法详解
    目录1、Teleport用法2、完成模态对话框组件3、组件的渲染前言: Vue 3.0 新增了一个内置组件 teleport ,主要是为了解决以下场景: 有时组件模板的一部分逻辑上属...
    99+
    2024-04-02
  • Vue3中的Teleport功能怎么使用
    这篇文章主要介绍“Vue3中的Teleport功能怎么使用”,在日常操作中,相信很多人在Vue3中的Teleport功能怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue3中的Teleport功能怎...
    99+
    2023-07-02
  • Vue3中Teleport 组件的原理是什么
    这篇文章将为大家详细讲解有关Vue3中Teleport 组件的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。使用场景业务开发的过程中,我们经常会封...
    99+
    2024-04-02
  • Vue中如何使用Teleport组件
    这篇文章主要介绍“Vue中如何使用Teleport组件”,在日常操作中,相信很多人在Vue中如何使用Teleport组件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue中如何使用Teleport组件”的疑...
    99+
    2023-07-05
  • Vue3中内置组件Teleport的基本使用与典型案例
    目录1. 基本概念1.1 简单理解1.2 典型案例2. 基础使用2.1 传送 DOM 节点2.2 传送组件2.3 禁用传送功能2.4 多个元素传送给一个节点总结1. 基本概念 1.1...
    99+
    2023-05-18
    vue3内置组件 vue3 teleport vue3内置teleport
  • 详解Vue3中Teleport的使用
    目录Teleport 的目的 Teleport 是怎样工作的 在本文中,我们将介绍: Teleport 的目的 Teleport 的例子 一些很有意思的代码...
    99+
    2024-04-02
  • vue2怎么实现vue3的teleport
    本篇内容主要讲解“vue2怎么实现vue3的teleport”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“vue2怎么实现vue3的teleport”吧!vue2实现vue3的teleport不...
    99+
    2023-06-30
  • vue3 table组件怎么使用
    基础表格首先开发table组件之前,先想好要用什么样式的api,因为笔者在生产工作中用的都是element,所以前面几个组件风格和element类似,但是这次不打算用element的风格了,打算换一种,直接展示:我们期望用户这样使用:<...
    99+
    2023-05-14
    Vue3 table
  • vue3 teleport的使用案例详解
    官网 https://cli.vuejs.org/zh/guide/ 有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app ...
    99+
    2024-04-02
  • Vue3复用组件怎么使用
    我们看到,createReusableTemplate 返回了一个 Tuple,即 define 和 reuse 的组件对,然后,通过上面的例子就可以在单文件里面复用多处代码了。还有,实际上还可以通过对象解构的方式返回一个 define 和...
    99+
    2023-05-20
    Vue3
  • Vue3全局组件通信之provide/inject怎么使用
    本篇内容介绍了“Vue3全局组件通信之provide/inject怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、前言顾名思义,爷...
    99+
    2023-07-06
  • vue3动态组件怎么使用
    这篇文章主要介绍“vue3动态组件怎么使用”,在日常操作中,相信很多人在vue3动态组件怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue3动态组件怎么使用”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-07-06
  • Vue3兄弟组件传值之mitt怎么安装使用
    本篇内容介绍了“Vue3兄弟组件传值之mitt怎么安装使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!比起 Vue 实例上的 EventB...
    99+
    2023-07-02
  • Vue3异步组件Suspense怎么使用
    今天小编给大家分享一下Vue3异步组件Suspense怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Suspense...
    99+
    2023-07-06
  • VueJs中怎么使用Teleport及组件嵌套层次结构是什么
    这篇文章主要讲解了“VueJs中怎么使用Teleport及组件嵌套层次结构是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“VueJs中怎么使用Teleport及组件嵌套层次结构是什么”吧...
    99+
    2023-07-05
  • Vue3怎么使用Vite打包组件库
    本文小编为大家详细介绍“Vue3怎么使用Vite打包组件库”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue3怎么使用Vite打包组件库”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。打包配置vite 专门提供...
    99+
    2023-07-05
  • vue3伸缩菜单组件怎么使用
    本篇内容介绍了“vue3伸缩菜单组件怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!效果图在components下面创建一个conta...
    99+
    2023-07-05
  • vue3异步组件怎么用
    这篇文章给大家分享的是有关vue3异步组件怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。 在vue3中,异步组件可以减少打包的结果,会将异步组件...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作