11-内建组件和模块

60 阅读3分钟

内建组件和模块主要包括KeepAlive组件、Teleport组件、Transition组件等

1、KeepAlive组件的实现原理

1.组件的激活与失活

KeepAlive组件可以避免频繁的创建、挂载、卸载组件的这一系列操作,如常用的Tab组件需要反复切换。

原理:卸载时,被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')
    // _deActivate和_activate
    instance._deActivate = (vnode) => {
      move(vnode, storageContainer)
    }
    instance._activate = (vnode, container, anchor) => {
      move(vnode, container, anchor)
    }
    return () => {
      //被KeepAlive的组件‘
      let rawVNode = slots.default()
      // 如果不是组件直接渲染
      if (typeof rawVNode.type !== 'object') {
        return rawVNode
      }
      //在挂载时先获取缓存的组件vnode
      const cachedVNode = cache.get(rawVNode.type)
      if (cachedVNode) {
        // 如果有缓存的内容,在执行激活操作
        //继承组件实例
        rawVNode.component = cachedVNode.component
        // 标记true
        rawVNode.keptAlive = true
      } else {
        cache.set(rawVNode.type, rawVNode)
      }
      rawVNode.shouldKeepAlive = true

      rawVNode.keepAliveInstance = instance

      return rawVNode
    }
  }
}

2.include和exclude

默认情况下,KeepAlive组件会缓存所有内部组件,但会出现不希望缓存所有内部组件,通过include来配置需要缓存的组件,通过exclude配置不缓存的组件

3.缓存管理

KeepAlive的缓存是通过一个Map对象进行实现。

综合上面两点的最终版本代码如下

const cache = new Map()
const KeepAlive = { 
  // 标识
  __isKeepAlive: true,
  props: {
    include: RegExp,
    exclude: RegExp
  },
  setup(props, { slots }) {
    // 创建一个缓存对象
    const instance = currentInstance
    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 () => {
      let rawVNode = slots.default()
      if (typeof rawVNode.type !== 'object') {
        return rawVNode
      }

      const name = rawVNode.type.name
      if (
        name &&
        (
          (props.include && !props.include.test(name)) ||
          (props.exclude && props.exclude.test(name))
        )
      ) {
        return rawVNode
      }

      const cachedVNode = cache.get(rawVNode.type)
      if (cachedVNode) {
        rawVNode.component = cachedVNode.component
        rawVNode.keptAlive = true
      } else {
        cache.set(rawVNode.type, rawVNode)
      }

      rawVNode.shouldKeepAlive = true
      rawVNode.keepAliveInstance = instance

      return rawVNode
    }
  }
}

2、Teleport组件的实现原理

1.Teleport组件解决的问题

主要用于:将指定内容渲染到特定的容器中

2.实现Teleport组件

Teleport组件需要有两个特殊选项__isTelsport和process

标识和进程,代码如下

const Teleport = {
  __isTeleport: true,
  process(n1, n2, container, anchor, internals) {
    const { patch, patchChildren, move } = internals
    if (!n1) {
      // 挂载
      const target = typeof n2.props.to === 'string'
        ? document.querySelector(n2.props.to)
        : n2.props.to
      n2.children.forEach(c => patch(null, c, target, anchor))
    } else {
      // 更新
      patchChildren(n1, n2, container)
      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))
      }
    }
  }
}

3、Transition组件的实现原理

核心原理:

1.当DOM元素被挂载时,动效附加到该DOM元素上

2.当DOM元素被卸载时,不立即卸载DOM元素,而是等到附加到该DOM元素上的动效执行完成后再卸载

实现代码如下

const Transition = {
  name: 'Transition',
  setup(props, { slots }) {
    return () => {
      const innerVNode = slots.default()

      innerVNode.transition = {
        beforeEnter(el) {
          el.classList.add('enter-from')
          el.classList.add('enter-active')
        },
        enter(el) {
          nextFrame(() => {
            el.classList.remove('enter-from')
            el.classList.add('enter-to')

            el.addEventListener('transitionend', () => {
              el.classList.remove('enter-to')
              el.classList.remove('enter-active')
            })
          })
        },
        leave(el, performRemove) {
          el.classList.add('leave-from')
          el.classList.add('leave-active')

          // document.body.offsetHeight

          nextFrame(() => {
            el.classList.remove('leave-from')
            el.classList.add('leave-to')

            el.addEventListener('transitionend', () => {
              el.classList.remove('leave-to')
              el.classList.remove('leave-active')

              performRemove()
            })
          })
        }
      }

      return innerVNode
    }
  }
}

总结

学习了三个内置组件

1、KeepAlive:避免组件的重复使用而导致的频繁挂载、卸载操作,可以标记需要和不需要缓存的组件

2、Teleport:把组件渲染到指定容器,可以作为渲染器的一部分存在

3、Transition:动效处理为原生DOM添加动效过渡