Vue3之Teleport实现原理

Teleport官方文档

源码位置:

runtime-core/src/components/Teleport

我们都知道渲染组件是由渲染器完成,Teleport当然也是一个组件,只不过它比较特殊而已。

特殊就在于该组件的渲染逻辑不应该放在渲染器中,因为如果没有使用到该组件,通过树摇最终的构建包不会包含这部分代码。另外也是为了给渲染器 瘦身 哈!

所以需要在patch函数中判断是否是Teleport组件,再将控制权交还给Teleport组件,另外会再将一些渲染器的方法也传递给Teleport

js 复制代码
else if (typeof type === 'object' && type.__isTeleport) {
   // 组件选项中如果存在 __isTeleport 标识,则它是 Teleport 组件,
   // 调用 Teleport 组件选项中的 process 函数将控制权交接出去
   type.process(n1, n2, container, anchor, {
   patch,
   patchChildren,
   unmount,
   move(vnode, container, anchor) {
   insert(vnode.component ? vnode.component.subTree.el :
  ode.el, container, anchor)
   }
   })
   }

对于组件来说,例如keppAlive组件的子节点会被编译为插槽内容, 不过对于 Teleport 组件来说,直接将其子节点编译为一个数组即可

js 复制代码
//代表Teleport组件的VNode
{
 type: Teleport,
 // 以普通 children 的形式代表被 Teleport 的内容
 children: [
 { type: 'h1', children: 'Title' },
 { type: 'p', children: 'content' }
 ]
 }

如果是第一次挂载,需要根据Teleport组件上的to属性找到挂载点,接着将组件内的元素循环放在挂载点之下

js 复制代码
process(n1, n2, container, anchor, internals) {
  // 通过 internals 参数取得渲染器的内部方法
  const { patch } = internals
  // 如果旧 VNode n1 不存在,则是全新的挂载,否则执行更新
  if (!n1) {
    // 挂载
    // 获取容器,即挂载点
    const target = typeof n2.props.to === 'string'
      ? document.querySelector(n2.props.to)
      : n2.props.to
    // 将 n2.children 渲染到指定挂载点即可
    n2.children.forEach(c => patch(null, c, target, anchor))
  }
  else {
    //更新
  }
}

对于更新,只需要调用patchChildren比较子节点并替换。但是如果是Teleport组件的to属性改变了,代表要修改挂载点。因为子节点已经被替换,所以只需要移动到新的挂载点即可。

但是对于组件(vnode.component.subTree.el)和普通标签(vnode.el)实例存放的位置不一样,需要区分.所以渲染器传递的move函数封装了insert函数返回给Teleport使用,另外对于文本节点,片段也需要判断,这里只介绍这两种情况。

js 复制代码
else {
      // 更新
      patchChildren(n1, n2, container);
      // 如果新旧 to 参数的值不同,则需要对内容进行移动
      if (n2.props.to !== n1.props.to) {
        // 获取新的容器
        const newTarget =
          typeof n2.props.to === "string"
            ? document.querySelector(n2.props.to)
            : n2.props.to;
        // 移动到新的容器
        n2.children.forEach((c) => move(c, newTarget));
      }
    }
js 复制代码
move(vnode, container, anchor) {
  insert(
    vnode.component
      ? vnode.component.subTree.el // 移动一个组件
      : vnode.el, // 移动普通元素
    container,
    anchor
  );
}
相关推荐
IT_陈寒29 分钟前
SpringBoot自动配置揭秘:90%开发者不知道的核心原理
前端·人工智能·后端
huangyiyi6666631 分钟前
webpack + Vite
前端·webpack·node.js
im_AMBER34 分钟前
订阅模式实现字符数统计
前端·typescript·前端框架·编辑器
北寻北爱35 分钟前
axios
开发语言·前端·javascript
Nuopiane36 分钟前
Mypal3(9)
前端·javascript·数据库
筱璦42 分钟前
期货软件开发 - 交易报表
前端·windows·microsoft·报表·期货
暴躁网友w43 分钟前
掌握Fetch与Flask交互:让前端表单提交更优雅的动态之道
前端·flask·交互
木斯佳1 小时前
前端八股文面经大全:腾讯前端暑期提前批一、二、三面面经(上)(2026-03-04)·面经深度解析
前端
嘉琪0011 小时前
Day4 完整学习包(this 指向)——2026 0313
前端·javascript·学习
前端小菜鸟也有人起1 小时前
Vue3父子组件通信方法总结
前端·javascript·vue.js