LRU算法在Vue内置组件keep-alive中的使用

vue的keep-alive内置组件的使用也是使用了改算法,源码如下:

export default {
  name: "keep-alive",
  // 抽象组件属性 ,它在组件实例建立父子关系的时候会被忽略,发生在 initLifecycle 的过程中
  abstract: true,
  props: {
    // 被缓存组件
    include: patternTypes,
    // 不被缓存组件
    exclude: patternTypes,
    // 指定缓存大小
    max: [String, Number]
  },
  created() {
    // 初始化用于存储缓存的 cache 对象
    this.cache = Object.create(null);
    // 初始化用于存储VNode key值的 keys 数组
    this.keys = [];
  },
  destroyed() {
    for (const key in this.cache) {
      // 删除所有缓存
      pruneCacheEntry(this.cache, key, this.keys);
    }
  },
  mounted() {
    // 监听缓存(include)/不缓存(exclude)组件的变化
    // 在变化时,重新调整 cache
    // pruneCache:遍历 cache,如果缓存的节点名称与传入的规则没有匹配上的话,就把这个节点从缓存中移除
    this.$watch("include", val => {
      pruneCache(this, name => matches(val, name));
    });
    this.$watch("exclude", val => {
      pruneCache(this, name => !matches(val, name));
    });
  },
  render() {
    // 获取第一个子元素的 vnode
    const slot = this.$slots.default;
    const vnode: VNode = getFirstComponentChild(slot);
    const componentOptions: ?VNodeComponentOptions =
      vnode && vnode.componentOptions;
    if (componentOptions) {
      // name 不在 inlcude 中或者在 exlude 中则直接返回 vnode,否则继续进行下一步
      // check pattern
      const name: ?string = getComponentName(componentOptions);
      const { include, exclude } = this;
      if (
        // not included
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode;
      }

      const { cache, keys } = this;
      // 获取键,优先获取组件的 name 字段,否则是组件的 tag
      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;

      // --------------------------------------------------
      // 下面就是 LRU 算法了,
      // 如果在缓存里有则调整,
      // 没有则放入(长度超过 max,则淘汰最近没有访问的)
      // --------------------------------------------------
      // 如果命中缓存,则从缓存中获取 vnode 的组件实例,并且调整 key 的顺序放入 keys 数组的末尾
      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance;
        // make current key freshest
        remove(keys, key);
        keys.push(key);
      }
      // 如果没有命中缓存,就把 vnode 放进缓存
      else {
        cache[key] = vnode;
        keys.push(key);
        // prune oldest entry
        // 如果配置了 max 并且缓存的长度超过了 this.max,还要从缓存中删除第一个
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode);
        }
      }

      // keepAlive标记位
      vnode.data.keepAlive = true;
    }
    return vnode || (slot && slot[0]);
  }
};

// 移除 key 缓存
function pruneCacheEntry (
  cache: VNodeCache,
  key: string,
  keys: Array<string>,
  current?: VNode
) {
  const cached = cache[key]
  if (cached && (!current || cached.tag !== current.tag)) {
    cached.componentInstance.$destroy()
  }
  cache[key] = null
  remove(keys, key)
}

// remove 方法(shared/util.js)
/**
 * Remove an item from an array.
 */
export function remove (arr: Array<any>, item: any): Array<any> | void {
  if (arr.length) {
    const index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}

实现一个自己的LRU算法

lru算法 的核心api(put get)和一个size最大容器值,本质是类似队列 put实现思路 1 是否存在,存在就先删除,再添加到队头 2 不存在,容量是否满了,删除最后一个队尾,再添加队头 get实现思路: 1.有就返回,同时插入队头 2.没有返回-1 时间复杂度O(1)

class LRU {
  constructor(size) {
    this.cache = new Map()
    this.size = size
  }
  put (key, val) {
    //存在
    if (this.cache.has(key)) {
      //删除
      this.cache.delete(key)
    } else {
      //不存在,容量是否满了
      if (this.size === this.cache.size) {
        //删除最后一个
        this.cache.delete(this.cache.keys().next().value) //拿到队尾的元素
      }
    }
    //插在队头
    this.cache.set(key, val)
  }
  get (key) {
    let val = this.cache.get(key)
    if (!val) {
      return -1
    }
    //访问了就需要放在队头
    this.put(key, val)
    return val
  }
}

另一种

//定义节点类
class Node {
    constructor(pre, next, value, key){
        this.pre = pre;
        this.next = next;
        this.value = value;
        this.key = key;
    }
}

//定义双向链表
class DoubleList {
    constructor(head, tail){
        this.head = head;
        this.tail = tail;
    }
}

class LRUCache {
    //构造函数,传入缓存容量
    constructor(max){
        this.max = max;
        this.map = new Map();
        let node = new Node(null, null, null, null);
        this.doubleList = new DoubleList(node, node);
    }

    /**
     * 获取缓存值
     * 不存在返回-1,存在返回对应value值,并将此节点移到尾巴
     * @param {*} key  key值
     */
    get(key){
        let node = this.map.get(key)
        if(!node){
            return -1;
        }else{
            this.moveNode2Tail(key, node);
            return node.value;
        }
    }

    /**
     * 插入缓存
     * 1.不存在对应key值,加到尾巴
     * 2.存在对应key值,更新此key值对应value并提到尾巴
     * 3.超出容量的话,去掉头部数据
     * @param {*} key  key值
     * @param {*} value  value
     */
    put(key, value) {
        let node = this.map.get(key);
        if(node){
            if(!node.next){
                node.value = value;
                return;
            }
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }
        let newNode = new Node(null, null, value, key);
        newNode.pre = this.doubleList.tail;
        this.doubleList.tail.next = newNode;
        this.doubleList.tail = newNode;
        this.map.set(key, newNode);
        if(this.map.size > this.max){
            this.map.delete(this.doubleList.head.next.key);
            this.doubleList.head.next = this.doubleList.head.next.next;
            this.doubleList.head.next.pre = this.doubleList.head;
        }
    }

    //将节点移到尾巴
    moveNode2Tail(key,node){
        if(!node.next){
            return;
        }
        //删除节点
        node.pre.next = node.next;
        node.next.pre = node.pre;
        this.map.delete(key)
        //新增尾巴节点
        let newNode = new Node(null, null, node.value, key);
        newNode.pre = this.doubleList.tail;
        this.doubleList.tail.next = newNode;
        this.doubleList.tail = newNode;
        this.map.set(key, newNode);
    }
}

以上就是LRU算法在Vue内置组件keep-alive中的使用的详细内容,更多关于Vue LRU算法的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解Vue2的diff算法

    前言 双端比较算法是vue2.x采用的diff算法,本篇文章只是对双端比较算法粗略的过程进行了一下分析,具体细节还是得Vue源码,Vue的源码在这 过程 假设当前有两个数组arr1和arr2 let arr1 = [1,2,3,4,5] let arr2 = [4,3,5,1,2] 那么其过程有五步 arr1[0] 和 arr2[0]比较 arr1[ arr1.length-1 ] 和 arr2[ arr2.length-1 ] 比较 arr1[0] 和 arr2[ arr2.length-1

  • 一篇文章带你搞懂Vue虚拟Dom与diff算法

    前言 使用过Vue和React的小伙伴肯定对虚拟Dom和diff算法很熟悉,它扮演着很重要的角色.由于小编接触Vue比较多,React只是浅学,所以本篇主要针对Vue来展开介绍,带你一步一步搞懂它. 虚拟DOM 什么是虚拟DOM? 虚拟DOM(Virtual   Dom),也就是我们常说的虚拟节点,是用JS对象来模拟真实DOM中的节点,该对象包含了真实DOM的结构及其属性,用于对比虚拟DOM和真实DOM的差异,从而进行局部渲染来达到优化性能的目的. 真实的元素节点: <div id="wr

  • 解析Vue 2.5的Diff算法

    DOM"天生就慢",所以前端各大框架都提供了对DOM操作进行优化的办法,Angular中的是脏值检查,React首先提出了Virtual Dom,Vue2.0也加入了Virtual Dom,与React类似. 本文将对于Vue 2.5.3版本中使用的Virtual Dom进行分析. updataChildren是Diff算法的核心,所以本文对updataChildren进行了图文的分析. 1.VNode对象 一个VNode的实例包含了以下属性,这部分代码在src/core/vdom/v

  • vue2.0中goods选购栏滚动算法的实现代码

    不多说,直接代码,以便以后重复利用: <script type="text/ecmascript-6"> import BScroll from 'better-scroll'; const ERR_OK = 0; export default { props: { sell: { type: Object } }, data() { return { goods: [], listHeight: [], scrollY: 0 }; }, computed: { curre

  • 详解为什么Vue中不要用index作为key(diff算法)

    前言 Vue 中的 key 是用来做什么的?为什么不推荐使用 index 作为 key?常常听说这样的问题,本篇文章带你从原理来一探究竟. 另外本文的结论对于性能的毁灭是针对列表子元素顺序会交换.或者子元素被删除的特殊情况,提前说明清楚,喷子绕道. 本篇已经收录在 Github 仓库,欢迎 Star: https://github.com/sl1673495/blogs/issues/39 示例 以这样一个列表为例: <ul> <li>1</li> <li>

  • Vue的transition-group与Virtual Dom Diff算法的使用

    开始 这次的题目看上去好像有点奇怪:把两个没有什么关联的名词放在了一起,正如大家所知道的,transition-group就是Vue的内置组件之一主要用在列表的动画上,但是会跟Virtual Dom Diff算法有什么特别的联系吗?答案明显是有的,所以接下来就是代码分解. 缘起 主要是最近对Vue的Virtual Dom Diff算法有点模糊了,然后顺手就打开了电脑准备温故知新:但是很快就留意到代码: // removeOnly is a special flag used only by <t

  • vue的diff算法知识点总结

    源码:https://github.com/vuejs/vue/blob/dev/src/core/vdom/patch.js 虚拟dom diff算法首先要明确一个概念就是diff的对象是虚拟dom,更新真实dom则是diff算法的结果 Vnode基类 constructor ( ... ) { this.tag = tag this.data = data this.children = children this.text = text this.elm = elm this.ns = u

  • vue diff算法全解析

    前言 我们知道 Vue 使用的是虚拟 DOM 去减少对真实 DOM 的操作次数,来提升页面运行的效率.今天我们来看看当页面的数据改变的时候,Vue 是如何来更新 DOM 的.Vue和React在更新dom时,使用的算法基本相同,都是基于 snabbdom. 当页面上的数据发生变化时,Vue 不会立即渲染.而是经过 diff 算法,判断出哪些是不需要变化的,哪些是需要变化更新的,只需要更新那些需要更新的 DOM 就可以了,这样就减少了很多不必要的 DOM 操作,大大提升了性能. Vue就使用了这样

  • 详解vue的diff算法原理

    我的目标是写一个非常详细的关于diff的干货,所以本文有点长.也会用到大量的图片以及代码举例,目的让看这篇文章的朋友一定弄明白diff的边边角角. 先来了解几个点... 1. 当数据发生变化时,vue是怎么更新节点的? 要知道渲染真实DOM的开销是很大的,比如有时候我们修改了某个数据,如果直接渲染到真实dom上会引起整个dom树的重绘和重排,有没有可能我们只更新我们修改的那一小块dom而不要更新整个dom呢?diff算法能够帮助我们. 我们先根据真实DOM生成一颗 virtual DOM ,当

  • 详解vue3.0 diff算法的使用(超详细)

    前言:随之vue3.0beta版本的发布,vue3.0正式版本相信不久就会与我们相遇.尤玉溪在直播中也说了vue3.0的新特性typescript强烈支持,proxy响应式原理,重新虚拟dom,优化diff算法性能提升等等.小编在这里仔细研究了vue3.0beta版本diff算法的源码,并希望把其中的细节和奥妙和大家一起分享. 首先我们来思考一些大中厂面试中,很容易问到的问题: 1 什么时候用到diff算法,diff算法作用域在哪里? 2 diff算法是怎么运作的,到底有什么作用? 3 在v-f

  • 基于Vue实现电商SKU组合算法问题

    前段时间,公司要做"添加商品"业务模块,这也算是电商业务里面的一个难点了. 令我印象最深的不是什么"组合商品"."关联商品"."关联单品",而是商品SKU的组合问题. 这个问题特别有意思,当时虽然大体上组合成功,总是有些小bug解决不了,然后手上又有别的任务就没仔细研究它. 后来过了一个月,空闲下来专门研究了下,终于把问题解决,有必要记录下这次体验. 先看下在业务中的效果(tips: 如看不清可放大浏览器) 这个相对来说比较麻

  • Vue实现开心消消乐游戏算法

    之前做过一个算法题,算法要求就是写一个开心消消乐的逻辑算法,当时也是考虑了一段时间才做出来.后来想了想,既然核心算法都有了,能不能实现一个开心消消乐的小游戏呢,于是花了两天时间做了一个小游戏出来. 效果展示# 先在这里放一个最终实现的效果,还是一个比较初级的版本,大家有什么想法欢迎评论哦 游戏规则: 初始时会给玩家十分的初始分,每拖动一次就减一分,每消除一个方块就加一分,直到最后分数为0游戏结束 任意两个方块都可以拖动 界面设计# 页面的布局比较简单,格子的数据是一个二维数组的形式,说到这里大家

随机推荐