Vuejs设计与实现8-异步组件与内建组件

十、异步组件

异步组件实现

直接在 vue 文件内使用 defineAsyncComponent 方法定义异步组件,并直接 import 导入

defineAsyncComponent 最简实现(异步实现实际上就是使用 promise 的 then 方法):

// 接收一异步加载器
function defineAsyncComponent(loader) {
  // 存储需要异步加载的组件
  let InnerComp = null;

  // 返回封装好的组件
  return {
    name: "AsyncComponentWrapper",
    setup() {
      // 异步组件是否加载成功
      const loaded = ref(false);

      // 执行加载器函数,返回一个 Promise 实例
      // 加载成功后,将加载成功的组件赋值给 InnerComp,并将 loaded 标记为 true,代表加载成功
      loader().then((c) => {
        InnerComp = c;
        loaded.value = true;
      });

      return () => {
        // 如果异步组件加载成功,则渲染该组件,否则渲染一个占位内容
        return loaded.value
          ? { type: InnerComp }
          : { type: Text, children: "" };
      };
    },
  };
}

超时问题

根据用户网络环境,应设置超时时长,当某组件加载时间超过该时长后就报错;

原理即设置一个定时器,在组件开始加载时计时,超过设定的 timeout 就报错,代码添加在 defineAsyncComponent 方法里


函数式组件

增补


十一、内建组件

keepalive

keepalive 是为了保证一个组件可以复用而无需每次都卸载重建浪费资源

被 keepalive 包裹的组件,会有以下两种状态:

  1. 卸载(deactivated)状态:组件并不是真的卸载,而是放到一个隐藏容器中
  2. 挂载(activated)状态:从隐藏容器中再拿出组件的过程

keepalive 实现代码

const KeepAlive = {
  // KeepAlive 组件独有的属性,用作标识
  __isKeepAlive: true,
  setup(props, { slots }) {
    // 创建一个缓存对象
    // key: vnode.type
    // value: vnode
    const cache = new Map()
    // 当前 KeepAlive 组件的实例
    const instance = currentInstance
    // 对于 KeepAlive 组件来说,它的实例上存在特殊的 keepAliveCtx 对象,该对象由渲染器注入
    // 该对象会暴露渲染器的一些内部方法,其中 move 函数用来将一段 DOM 移动到另一个容器中
    const { move, createElement } = instance.keepAliveCtx
    // 创建隐藏容器
    const storageContainer = createElement('div')
    // 卸载和挂载的方法
   instance._deActivate = (vnode) => {
     move(vnode, storageContainer)
   }
   instance._activate = (vnode, container, anchor) => {
     move(vnode, container, anchor)
   }

   return () => {
     // KeepAlive 的默认插槽就是要被 KeepAlive 的组件
     let rawVNode = slots.default()
     // 如果不是组件,直接渲染即可,因为非组件的虚拟节点无法被 KeepAlive
     if (typeof rawVNode.type !== 'object') {
       return rawVNode

     // 在挂载时先获取缓存的组件 vnode
     const cachedVNode = cache.get(rawVNode.type)
     if (cachedVNode) {
       // 如果有缓存的内容,则说明不应该执行挂载,而应该执行激活
       // 继承组件实例
       rawVNode.component = cachedVNode.component
       // 在 vnode 上添加 keptAlive 属性,标记为 true,避免渲染器重新挂载它
       rawVNode.keptAlive = true
     } else {
       // 如果没有缓存,则将其添加到缓存中,这样下次激活组件时就不会执行新的挂载动作了
       cache.set(rawVNode.type, rawVNode)

     // 在组件 vnode 上添加 shouldKeepAlive 属性,并标记为 true,避免渲染器真的将组件卸载
      rawVNode.shouldKeepAlive = true
      // 将 KeepAlive 组件的实例也添加到 vnode 上,以便在渲染器中访问
      rawVNode.keepAliveInstance = instance
      // 渲染组件 vnode
      return rawVNode
    }
  }
}

包含与否

keepalive 可以根据用户传入的数据决定是否要缓存组件;
include 属性:表示需要缓存的组件名
exclude 属性:表示不需要缓存的组件名

俩属性一般使用正则进行匹配,下面是 keepalive 的模板:

const keepalive = {
  _isKeepalive: true,
  props: {
    include: /regexp/,
    exclude: /regexp/,
  },
  setup(props, { slots }) {
    return () => {};
  },
};

缓存

当缓存不存在时会设置新的缓存,此时需要存在一个缓存阈值来控制缓存数量不能无限的增长下去;

可参考 JAVA 的 GC 中新老生代的方法,如果超出缓存阈值,那就清理掉最先使用的缓存;


teleport 组件

teleport 组件的作用:跨 DOM 层级渲染,避免 z-index 影响


teleport 最简实现

teleport 的使用模板,还有很多问题没有解决,这里仅提供参考!

const teleport = {
  _isTeleport: true,
  process(n1, n2, container, anchor, internals) {
    // 获取渲染器内部方法
    const { patch } = internals;

    // 若干旧vnode不存在,则挂载新的vnode
    if (!n1) {
      // 获取挂载点
      const target =
        typeof n2.props.to === "string"
          ? document.querySelector(n2.props.to)
          : n2.props.to;
      // 将新vnode的子节点挂载到指定挂载点
      n2.children.forEach((c) => patch(null, c, target, anchor));
    } else {
      // 更新代码
    }
  },
};

transition

transition 俩要素:
DOM 被挂载,则添加动效到该 DOM
DOM 被卸载,等动效执行完毕再卸载


实现原理

假设有如下代码

<Transition>
  <div>123</div>
</Transition>

transition 内部整体视为一个插槽 slot

故此为 transition 的基本实现源码

const Transition = {
  name: "Transition",
  setup(props, { slots }) {
    return () => {
      // 通过默认插槽获取过渡元素
      const innerVnode = slots.default();
      // 对应动效加载各个阶段的钩子函数
      innerVnode.transition = {
        beforeEnter(ele) {},
        enter(ele) {},
        leave(ele, performRemove) {},
      };
      return innerVnode;
    };
  },
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zhillery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值