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
  );
}
相关推荐
GIS程序媛—椰子5 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00111 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端14 分钟前
Content Security Policy (CSP)
前端·javascript·面试
木舟100918 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤439128 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
半开半落1 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt