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
  );
}
相关推荐
Myli_ing34 分钟前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风37 分钟前
前端 vue 如何区分开发环境
前端·javascript·vue.js
软件小伟1 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾1 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧1 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7012 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm2 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
幼儿园的小霸王2 小时前
通过socket设置版本更新提示
前端·vue.js·webpack·typescript·前端框架·anti-design-vue
疯狂的沙粒2 小时前
对 TypeScript 中高级类型的理解?应该在哪些方面可以更好的使用!
前端·javascript·typescript