Vue.js设计与实现(电子版)源码学习

1、KeepAlive 组件的实现原理

KeepAlive 的本质是缓存管理,再加上特殊的挂载/卸载逻
辑。
  首先,KeepAlive 组件的实现需要渲染器层面的支持。这是因为
被 KeepAlive 的组件在卸载时,我们不能真的将其卸载,否则就无法
维持组件的当前状态了。正确的做法是,将被 KeepAlive 的组件从原
容器搬运到另外一个隐藏的容器中,实现“假卸载”。当被搬运到隐
藏容器中的组件需要再次被“挂载”时,我们也不能执行真正的挂载
逻辑,而应该把该组件从隐藏容器中再搬运到原容器。这个过程对应
到组件的生命周期,其实就是 activated 和 deactivated。

Vue.js设计与实现(电子版)源码学习_第1张图片

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')

    // KeepAlive 组件的实例上会被添加两个内部函数,分别是 _deActivate和 _activate
    // 这两个函数会在渲染器中被调用
    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
    }
    }
 }

2、异步组件的实现原理

function defineAsyncComponent(options) {
  // options 可以是配置项,也可以是加载器
  if (typeof options === 'function') {
    // 如果 options 是加载器,则将其格式化为配置项形式
    options = {
      loader: options,
    };
  }

  const { loader } = options;

  let InnerComp = null;

  return {
    name: 'AsyncComponentWrapper',
    setup() {
      const loaded = ref(false);
      // 代表是否超时,默认为 false,即没有超时
      const timeout = ref(false);

      loader().then((c) => {
        InnerComp = c;
        loaded.value = true;
      });

      let timer = null;
      if (options.timeout) {
        // 如果指定了超时时长,则开启一个定时器计时
        timer = setTimeout(() => {
          // 超时后将 timeout 设置为 true
          timeout.value = true;
        }, options.timeout);
      }
      // 包装组件被卸载时清除定时器
      onUmounted(() => clearTimeout(timer));

      // 占位内容
      const placeholder = { type: Text, children: '' };

      return () => {
        if (loaded.value) {
          // 如果组件异步加载成功,则渲染被加载的组件
          return { type: InnerComp };
        } else if (timeout.value) {
          // 如果加载超时,并且用户指定了 Error 组件,则渲染该组件
          return options.errorComponent ? { type: options.errorComponent } : placeholder;
        }
        return placeholder;
      };
    },
  };
}

Vue.js设计与实现(电子版)源码学习_第2张图片

本文来自于 Vue.js设计与实现电子版的学习
Vue.js设计与实现(电子版)源码学习_第3张图片

如果有需要,可添加 微信 mini_pear_libb,将会分享电子版

你可能感兴趣的:(vue.jsvue3)