关于keep-alive的源码解读

168 阅读2分钟

注:以下是个人理解、如有不对还望指正!

keep-alive

keep-alive主要用做组件内容缓存,当切换组件是不会对当前组件进行卸载。要是有`include、exclude、max`
三个属性;前两个属性允许`keep-alive`有条件的进行缓存;`max`可以定义组件最大的缓存个数,如果超过了这
个个数的话,使用LRU策略,策略根据数据的历史访问记录来进行淘汰数据。LRU 策略的设计原则是,如果一个
数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也会小。就是说,当限定的空间已存满数据时,
应当把最长时间内没有被重复访问到的数据淘汰,在下次存储数据时将最长时间没有访问到的数据进行淘汰

源码分析

下面展示部分源码及其注释

  • 获取组件的名称
function getComponentName (opts: ?VNodeComponentOptions): ?string {
  return opts && (opts.Ctor.options.name || opts.tag)
}

// 判断是否匹配成功 根据 include exclude 判断
function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
  if (Array.isArray(pattern)) { // 判断是否为数组
    return pattern.indexOf(name) > -1

  } else if (typeof pattern === 'string') { // 判断是否为字符串
    return pattern.split(',').indexOf(name) > -1

  } else if (isRegExp(pattern)) { // 是否为正则
    return pattern.test(name)
  }
  /* istanbul ignore next */
  return false
}
  • 修正缓存
  // 当前 keep-alive 实例  缓存 key 数组 当前虚拟NODE
  const { cache, keys, _vnode } = keepAliveInstance

  // 对于不需要缓存的组件及时清除
  for (const key in cache) {
    const entry: ?CacheEntry = cache[key]
    if (entry) {
      const name: ?string = entry.name
      if (name && !filter(name)) {
        pruneCacheEntry(cache, key, keys, _vnode)
      }
    }
  }
}
  • 清除缓存
  cache: CacheEntryMap,
  key: string,
  keys: Array<string>,
  current?: VNode
) {
  const entry: ?CacheEntry = cache[key] 
  // 如果缓存对象 没有实例或者 当前缓存的标签不等于当前标签
  if (entry && (!current || entry.tag !== current.tag)) {
    // 强制销毁
    entry.componentInstance.$destroy()
  }
  // 清空
  cache[key] = null
  // 删除
  remove(keys, key)
}
  • render
    // 获取插槽的默认元素 this.$slots
    const slot = this.$slots.default
    // 获取第一个缓存的组件
    const vnode: VNode = getFirstComponentChild(slot)
    // 拿出当前组件的 options 选项
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern

      // 获取组件名称
      const name: ?string = getComponentName(componentOptions)

      // 获取组件传过来的 props 包含 未包含
      const { include, exclude } = this
      if (
        // not included
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }

      const { cache, keys } = this
      const key: ?string = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      
      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
         // LRU 缓存淘汰算法
        remove(keys, key)
        keys.push(key)
      } else {
        // delay setting the cache until update
        // 缓存当前的组件实例
        this.vnodeToCache = vnode
        // 缓存当前组件的 key
        this.keyToCache = key
      }

      vnode.data.keepAlive = true
    }
    // 返回组件实例 返回插槽第一个元素
    return vnode || (slot && slot[0])
  }
}

总结

`keep-alive` 组件是抽象组件,它只对包裹的子组件做处理,主要是根据LRU策略缓存组件 `VNode`,最后在
`render` 时返回子组件的 `VNode`。缓存渲染过程会更新 `keep-alive` 插槽,重新再 `render` 一次,
从缓存中读取之前的组件 `VNode` 实现状态缓存。

!如有侵权请联系删除