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
  );
}
相关推荐
im_AMBER16 分钟前
Web 开发 27
前端·javascript·笔记·后端·学习·web
蓝胖子的多啦A梦42 分钟前
低版本Chrome导致弹框无法滚动的解决方案
前端·css·html·chrome浏览器·版本不同造成问题·弹框页面无法滚动
玩代码43 分钟前
vue项目安装chromedriver超时解决办法
前端·javascript·vue.js
訾博ZiBo1 小时前
React 状态管理中的循环更新陷阱与解决方案
前端
StarPrayers.1 小时前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
一壶浊酒..2 小时前
ajax局部更新
前端·ajax·okhttp
DoraBigHead2 小时前
React 架构重生记:从递归地狱到时间切片
前端·javascript·react.js
彩旗工作室3 小时前
WordPress 本地开发环境完全指南:从零开始理解 Local by Flywhee
前端·wordpress·网站
iuuia3 小时前
02--CSS基础
前端·css
kyle~3 小时前
Qt---setAttribute设置控件或窗口的内部属性
服务器·前端·c++·qt