vue keep-alive 内置组件与 LRU缓存机制

16 篇文章 1 订阅

在刷leetcode 的时候,遇到了一道题 LRU缓存机制 ,和vue keep-alive 里的缓存机制是一样的,就单独把这个拿出来,然后 顺便 把 keep-alive 学习一遍

1、Keep-alive组件


function pruneCacheEntry (
  cache: VNodeCache,
  key: string,
  keys: Array<string>,
  current?: VNode
) {
  // 把过期了的 vnode 给销毁了
  // 如果当前 这个 vnode 正在展示当中,也就是 current ,那就不销毁
  const cached = cache[key]
  if (cached && (!current || cached.tag !== current.tag)) {
    cached.componentInstance.$destroy()
  }
  // 但是还是 手动 把 这个节点给 清空,方便后期 浏览器回收内存
  cache[key] = null
  // 删除 keys 数组中的 key
  remove(keys, key)
}
export default {
  name: 'keep-alive',
  abstract: true,

  // 接受的 三个参数
  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },

  created () {
    // 创建内部变量
    // 在 注意,这两个变量不是响应式的,只是简单地挂在了 当前 vue 实例上面
    this.cache = Object.create(null)
    this.keys = []
  },

  destroyed () {
    // 当 当前组件销毁的时候,把所有的 缓存清除
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },

  mounted () {
    // 监听 include exclude 这两个 props,然后动态的删除修改缓存的数据
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  },
  // 这里使用的就是函数式组件,没有 template 的那种
  // 这里返回的是一个 vnode 节点,而 $slot 就是一个 vnode 节点
  render () {
    // 这里就是通过 $slot 获得 的 需要缓存的 组件
    const slot = this.$slots.default
    // 从 其中 获取第一个进行缓存,如上文 所示,default 是一个 数组,从这个数组里面拿到第一个
    const vnode: VNode = getFirstComponentChild(slot)
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // 获取 缓存的组件名称
      const name: ?string = getComponentName(componentOptions)
      const { include, exclude } = this
      // 如果 不再 included 中,或者在 exclude 之内,就不执行缓存
      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
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      // keep-alive 的内置属性之一, cache 和 keys
      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        // 这里的作用其实也很简单,就是 将 最新使用的节点 名称 放到 缓存 keys 数组的最前面
        // 这样就可以做到 当 设置了 最多缓存的组件之后,超出 则删除 最久没使用的节点
        remove(keys, key)
        keys.push(key)
      } else {
        // 这里 缓存了 当前 vue 页面的 实例,
        cache[key] = vnode
        keys.push(key)
        // 删除最老的节点
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}
  1. 创建内部变量 keys 数组 和 cache 对象 (这里就是LRU缓存机制) 的关键
  2. 通过 $slot 获取 注入的组件,并且只获取 第一个默认组件
  3. inculde 或者 exclude 存在的情况下,匹配 inculde 参数 和 exclude 参数,如果 不再 include 或者 在 enclude 中,不进行缓存
  4. 获取节点名称,或者 更具节点 cid 等信息拼出当前 组件名称
  5. 使用 LRU 缓存机制进行缓存,并更具 max 参数,删除 最久没访问的节点

2、LRU 缓存 leetcode 

 

/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
  // 和 上面的代码一样,一个缓存 对象,一个数组,一个 最大值
  this.capacity = capacity;
  this.cache = {}
  this.cacheArr = []
};

/** 
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
  // 获取的时候,如果当前 有缓存,那么把缓存拿出来,并且把 当前的 key 拿到最前面
  const index = this.cacheArr.indexOf(key)
  if (index !== -1) {
    this.cacheArr.splice(index, 1)
    this.cacheArr.unshift(key)
  }
  return this.cache[key] || -1
};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
  // 存入 数据
  this.cache[key] = value
  // 如果 缓存的数组中存在,说明以前已经缓存过了
  // 就把 当前的 key 放到最前面,变成最新鲜的
  const index = this.cacheArr.indexOf(key)
  if (index !== -1) {
    this.cacheArr.splice(index, 1)
  }
  this.cacheArr.unshift(key)
  // 同时检验 是否超出了最大长度,超出了的话,把数据清除
  if (this.cacheArr.length > this.capacity) {
    const key = this.cacheArr.slice(-1)[0]
    this.cache[key] = null
    this.cacheArr.pop()
  }
};
/**
 * Your LRUCache object will be instantiated and called as such:
 * var obj = new LRUCache(capacity)
 * var param_1 = obj.get(key)
 * obj.put(key,value)
 */
  1. 这里的思路就是使用了 keep-alive 组件里面的缓存方式
  2. 创建一个 缓存的对象,用来进行存储
  3. 创建一个存放缓存 key 的数组,用来校验当前 存放数据的 ‘新鲜度’
  4. 当 有新数据,或者 数据被读取了之后,把 对应的 key 放到 数组 的最前面,变成最新鲜的,所以只需要维持一个 key 的数组就能知道当前数据的新鲜程度
  5. 查看 当前 keys 数组的长度来检查是否超出缓存的长度,超出了,删除 key 和对应的缓存

 

最后从结果上来看,我的实现显然不是最优解,但是 可以和 vue 源码对应起来就让我觉得别有收获

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值