我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Vue3之Teleport组件如何使用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Vue3之Teleport组件如何使用

这篇文章主要介绍了Vue3之Teleport组件如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue3之Teleport组件如何使用文章都会有所收获,下面我们一起来看看吧。

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/class="lazy" data-src/components/Teleport.tsexport 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/class="lazy" data-src/renderer.tsconst 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/class="lazy" data-src/components/Teleport.tsif (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/class="lazy" data-src/components/Teleport.tselse {  // 更新 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/class="lazy" data-src/components/Teleport.tsfunction 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/class="lazy" data-src/components/Teleport.ts// 服务端渲染 Teleportfunction 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组件如何使用”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Vue3之Teleport组件如何使用”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Vue3之Teleport组件如何使用

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

下载Word文档

猜你喜欢

Vue3之Teleport组件如何使用

这篇文章主要介绍了Vue3之Teleport组件如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue3之Teleport组件如何使用文章都会有所收获,下面我们一起来看看吧。Teleport 组件解决的问
2023-07-06

Vue3之Teleport组件怎么使用

Teleport组件解决的问题版本:3.2.31如果要实现一个“蒙层”的功能,并且该“蒙层”可以遮挡页面上的所有元素,通常情况下我们会选择直接在标签下渲染“蒙层”内容。如果在Vue.js2中实现这个功能,只能通过原生DOMAPI来手动搬运DOM元素实现,这就会使得元素的渲染与Vue.js的渲染机制脱节,并会导致各种可预见或不可遇见的问题。Vue.js3中内建的Teleport组件,可以将指定内容渲染到特定容器中,而不受DOM层级的限制。可以很好的解决这个问题。下面,我们来看看Teleport组件
2023-05-14

Vue中如何使用Teleport组件

这篇文章主要介绍“Vue中如何使用Teleport组件”,在日常操作中,相信很多人在Vue中如何使用Teleport组件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue中如何使用Teleport组件”的疑
2023-07-05

vue3 table组件如何使用

今天小编给大家分享一下vue3 table组件如何使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。基础表格首先开发tabl
2023-07-06

Vue3中内置组件Teleport的基本使用与典型案例

Teleport是一种能够将我们的模板移动到DOM中Vueapp之外的其他位置的技术,下面这篇文章主要给大家介绍了关于Vue3中内置组件Teleport的基本使用与典型案例的相关资料,需要的朋友可以参考下
2023-05-18

vue3动态组件如何使用

这篇“vue3动态组件如何使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue3动态组件如何使用”文章吧。问题:为什么v
2023-07-05

从0搭建Vue3组件库之如何使用Vite打包组件库

这篇文章主要介绍了从0搭建Vue3组件库之如何使用Vite打包组件库,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-03-07

Vue3异步组件Suspense如何使用

这篇文章主要介绍了Vue3异步组件Suspense如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue3异步组件Suspense如何使用文章都会有所收获,下面我们一起来看看吧。Suspense组件官网中
2023-07-05

Vue3中如何使用defineCustomElement定义组件

本篇内容主要讲解“Vue3中如何使用defineCustomElement定义组件”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue3中如何使用defineCustomElement定义组件”
2023-07-04

vue基于Teleport如何实现Modal组件

这篇文章将为大家详细讲解有关vue基于Teleport如何实现Modal组件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1.认识Teleport像我们如果写Modal组件、Message组件、Loadi
2023-06-15

VueJs中如何使用Teleport及组件嵌套层次结构详解

这篇文章主要为大家介绍了VueJs中如何使用Teleport及组件嵌套层次结构详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-14

如何使用vue3+TS实现简易组件库

这篇文章主要为大家展示了“如何使用vue3+TS实现简易组件库”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何使用vue3+TS实现简易组件库”这篇文章吧。前置首先下载vue-cli,搭建我们
2023-06-29

如何使用Vue3+TS实现语音播放组件

这篇文章主要介绍如何使用Vue3+TS实现语音播放组件,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!该功能将使用vue3 + TS来实现语音播放组件,使用什么技术不重要,重要的是看懂了核心逻辑后,通过原生js、rea
2023-06-29

Vue3全局组件通信之provide/inject怎么使用

本篇内容介绍了“Vue3全局组件通信之provide/inject怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、前言顾名思义,爷
2023-07-06

如何使用Vue3开发一个Pagination公共组件

这篇文章主要讲解了“如何使用Vue3开发一个Pagination公共组件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何使用Vue3开发一个Pagination公共组件”吧!要实现的功能属
2023-06-25

Vue3 Element Plus中el-form表单组件如何使用

这篇文章主要讲解了“Vue3 Element Plus中el-form表单组件如何使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Vue3 Element Plus中el-form表单组件
2023-07-06

vue3 table组件怎么使用

基础表格首先开发table组件之前,先想好要用什么样式的api,因为笔者在生产工作中用的都是element,所以前面几个组件风格和element类似,但是这次不打算用element的风格了,打算换一种,直接展示:我们期望用户这样使用:constdataList=[{id:1,name:&#39;《JavaEE企业应用实战》&#39;,author:&#39;dev1ce&#39;,price:&#39;10.22&#39;,desc:&#3
2023-05-14

Android Jetpack组件之ViewModel如何使用

本文小编为大家详细介绍“Android Jetpack组件之ViewModel如何使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android Jetpack组件之ViewModel如何使用”文章能帮助大家解决疑惑,下面跟着小编的思路
2023-07-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录