# vue源码 keep-alive

# 初次渲染

  • 不会重复渲染相同的组件,只会利用初次渲染保留的缓存去更新节点
  • 当路由切换的时候,被包裹的组件不会被销毁,重新进入时不会重新创建实例,会从缓存里面读取
  • keep-alive的Vnode会剔除多余的属性内容,由于它在其他属性在组件内部并没有意义,例如class样式

# keep-alive

  • vue在组件实例创建的时候会根据abstract属性决定是否忽略某个组件
  • keep-alive : abstract:true, vue跳过了它
export default {
  name: 'keep-alive',
  abstract: true,

  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },

  methods: {
    cacheVNode() {
      const { cache, keys, vnodeToCache, keyToCache } = this
      if (vnodeToCache) {
        const { tag, componentInstance, componentOptions } = vnodeToCache
        cache[keyToCache] = {
          name: getComponentName(componentOptions),
          tag,
          componentInstance,
        }
        keys.push(keyToCache)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
        this.vnodeToCache = null
      }
    }
  },

  created () {
    this.cache = Object.create(null) //缓存对象
    this.keys = []//里面保存 vnode.componentInstance = cache[key].componentInstance
  },

# createComponent

  • /vdom/create-component.js
export function createComponent{
    ...
      if (isTrue(Ctor.options.abstract)) {
   // 只保留slot属性,其他标签属性都被移除,在vnode对象上不再存在
    const slot = data.slot
    data = {}
    if (slot) {
      data.slot = slot
    }
  }
}
  • patch执行阶段会调用createElm创建真实dom,在创建节点途中,keep-alivevnode对象会被认定是一个组件Vnode,因此针对组件Vnode又会执行createComponent函数,它会对keep-alive组件进行初始化和实例化

# createComponent

  • patch.js
  • isReactivated用来判断组件是否缓存
  • 首次:vnode.componentInstance 空,keepalive:true,走创建vnode
  • 第二次,已经缓存,把上一次的dom插入即可
  function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
      var i = vnode.data
      if (isDef(i)) {
        // isReactivated用来判断组件是否缓存。
        var isReactivated = isDef(vnode.componentInstance) && i.keepAlive;
        if (isDef(i = i.hook) && isDef(i = i.init)) {
            // 执行组件初始化的内部钩子 init
          i(vnode, false /* hydrating */);
        }
        if (isDef(vnode.componentInstance)) {
          // 其中一个作用是保留真实dom到vnode中
          initComponent(vnode, insertedVnodeQueue);
          insert(parentElm, vnode.elm, refElm);
          if (isTrue(isReactivated)) {
            reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);
          }
          return true
        }
      }
    }

# 想局部刷新怎么办

  • beforeRouteEnter --> created --> mounted --> activated --> deactivated
  • 用钩子 activated
Last Updated: 2022/6/26 上午11:43:14