Vue 内置组件 keep-alive 中 LRU 缓存淘汰策略和实现

LRU(Least Recently Used,最近最少使用)是通过记录缓存项的访问顺序来决定淘汰的策略:当缓存满时,移除最久未被使用的项。

核心概念:

  • 缓存存储 :使用 Map 存储键值对,用于快速访问缓存内容。
  • 访问顺序跟踪 :通过 双向链表(LinkedList) 跟踪缓存的访问顺序,访问过的元素会被移动到链表的前端,最久未访问的元素位于链表的尾部。
  • 淘汰机制:缓存容量满时,删除链表尾部的元素,即最少使用的元素。

代码实现:

  1. LRUCache 类 :负责管理缓存,使用 Map 存储键值对,使用 LinkedList 跟踪访问顺序。
  2. LinkedList 类:双向链表,用于按访问顺序排列缓存项,支持移动元素到链表前端、删除尾部元素等操作。
  3. Node 类:链表节点,用于存储缓存项。

工作方式:

  • get(key):如果缓存存在,移至链表前端,表示最近使用。
  • put(key, value):插入新缓存或更新现有缓存,满时淘汰尾部项。

代码示例:

javascript 复制代码
// 双向链表节点定义
class Node {
  constructor(value) {
    this.value = value; // 节点值
    this.prev = null;   // 指向前一个节点
    this.next = null;   // 指向后一个节点
  }
}

// LRUCache 类实现 LRU 缓存
class LRUCache {
  constructor(maxSize) {
    this.maxSize = maxSize;
    this.cache = new Map(); // Map 存储缓存,保证插入顺序
    this.list = new LinkedList();// 链表记录访问顺序
  }
 
  // 获取缓存值	
  get(key) {
    if (this.cache.has(key)) { // 如果缓存中存在该 key
      this.list.moveToFront(key); // 将该 key 移动到链表头
      return this.cache.get(key); // 返回缓存值
    }
    return null; // 如果没有找到,返回 null
  }
  
  // 插入或更新缓存
  put(key, value) {
    // 如果缓存中已经有该 key,将该项移到链表前端
    if (this.cache.has(key)) {
      this.list.moveToFront(key);
    } else {
      if (this.cache.size >= this.maxSize) { // 如果缓存已满
        // 移除尾部的最久未使用项,从缓存中删除该项
        const lastKey = this.list.removeLast();
        this.cache.delete(lastKey);
      }
      this.list.addAtFront(key); // 将新项添加到链表头
    }
    this.cache.set(key, value); // 更新缓存的值
  }
}

// 双向链表实现
class LinkedList {
  constructor() {
    // 初始化链表为空
    this.head = this.tail = null;  // 初始化链表为空
  }
 
  // 将节点插入到链表前端
  addAtFront(value) {
    const newNode = new Node(value); // 创建新节点
    if (this.head) { // 1.如果链表非空:新节点的 next 指向当前头节点。当前头节点的 prev 指向新节点
      newNode.next = this.head;
      this.head.prev = newNode;
    } else {
      this.tail = newNode; // 2.如果链表为空,新的节点是尾节点
    }
    this.head = newNode; // 3.新节点为头节点
  }
 
  // 将某个节点移动到链表头
  moveToFront(value) {
    const node = this.find(value); // 查找节点
    if (node === this.head) return; // 如果该节点已在头部,直接返回   
    // 更新 node 的前后节点,使其从链表中移除。
   	// 当前节点的前一个节点 node.prev 的 next 指针,指向当前节点的下一个节点 node.next
    if (node.prev) node.prev.next = node.next;
    // 当前节点的下一个节点 node.next 的 prev 指针,指向 当前节点的前一个节点 node.prev
    if (node.next) node.next.prev = node.prev;

	// 如果是尾节点,更新尾节点
    if (this.tail === node) this.tail = node.prev; 
    // 将节点插入到链表前端
    this.addAtFront(value);
  }
 
  // 移除链表尾部节点并返回其 key
  removeLast() {
    const value = this.tail.value; // 获取尾部节点的值
   
    if(this.head === this.tail) {  // 如果链表只有一个节点
	  this.head = this.tail = null // 清空链表
	} else {
	  this.tail = this.tail.prev // 更新尾节点
	  this.tail.next = null // 断开尾节点与原节点的链接
	}
    return value; // 返回移除值
  }
 
  // 查找链表中某个节点
  find(value) {
    let current = this.head;
    // 遍历链表,找到目标节点
    while (current && current.value !== value) {
      current = current.next;
    }
    return current;
  }
}

注释解释

  • LRUCache 类:实现了缓存的主要功能,包括存储数据、获取数据、添加新数据、以及缓存淘汰。其中 get 和 put 方法:实现了缓存读取和更新逻辑,并保证缓存的顺序和容量。
  • LinkedList 类:双向链表,用来维护缓存访问顺序。链表的头节点表示最近使用的缓存项,尾节点表示最久未使用的缓存项。
  • Node 类:链表中的节点,每个节点包含值以及指向前后节点的指针。

操作示例:

javascript 复制代码
const cache = new LRUCache(3);
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
cache.get('key1'); // 最近使用过,移到前端
cache.put('key4', 'value4'); // 超过最大容量,移除最后使用的key3

LRU 缓存淘汰策略总结:

  1. 缓存项访问顺序:每次访问都会将该缓存项移到前端,表示最近使用。
  2. 满缓存时淘汰:如果缓存超出最大容量,淘汰链表尾部的最久未访问项(LRU)。
  3. 效率 :Map 提供 O(1) 时间复杂度的缓存存取,LinkedList 保证了 O(1) 的插入、移动和删除操作。

记忆要点:

  • LRU:最近最少使用的缓存项会被淘汰。
  • 双向链表:用来维护缓存项的访问顺序,确保 O(1) 时间复杂度。
  • 缓存淘汰:超出容量时,淘汰最久未访问的缓存项。
相关推荐
程序员Sunday3 小时前
Vue 3 响应式原理是什么?Proxy、ref 和 reactive 有什么区别?
前端·javascript
mldong3 小时前
一个 npm 包,九页审批中心:Vue3 项目接上工作流引擎
前端·javascript·vue.js
高级程序源11 小时前
django校园寻物通失物招领平台32261-计算机课程设计、毕业设计
vue.js·python·mysql·小程序·django·flask·课程设计
WeiXin_DZbishe12 小时前
基于springboot宠物领养系统-计算机毕设 附源码【课程设计】94217
vue.js·spring boot·python·django·php·课程设计
架构技术专栏12 小时前
Agent 工具调用:从工具声明到执行反馈
前端·人工智能·后端
别动我齐刘海13 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
志尊宝13 小时前
Vue3 零基础每日笔记(071):暗黑模式与主题切换——CSS 变量 + Element Plus 暗黑方案
前端·css·笔记·vue·html·前端开发·软件开发
WeiXin_DZbishe14 小时前
基于springboot城市公交实时运营与调度系统-计算机毕设 附源码【课程设计】66173
java·vue.js·spring boot·后端·python·php·课程设计
刘发财14 小时前
前端如何生成10000页PDF文件?
前端·javascript·开源
WeiXin_DZbishe14 小时前
基于Springboot+AI的kuall音乐广场的设计与实现-计算机毕设 附源码【课程设计】12715
vue.js·人工智能·spring boot·后端·python·php·课程设计