keep-alive原理

一、Keep-Alive简单介绍

Vue 中的 Keep-Alive 是一个非常有用的内置组件,用于缓存组件实例,以提高性能和用户体验。

1.Keep-Alive 的基本概念

Keep-Alive 是 Vue 的一个抽象组件,它不会渲染出实际的 DOM 元素,而是将其包裹的动态组件进行缓存。当组件在 Keep-Alive 内被切换时,它的状态会被保留,避免重新渲染。

2.Keep-Alive 的主要作用

性能优化:减少组件的重复渲染,提高应用性能。
状态保持:在组件切换时保留组件状态,提升用户体验。
减少 HTTP 请求:对于需要远程数据的组件,可以减少重复的 HTTP 请求。

3.Keep-Alive 的工作原理

Keep-Alive 的工作原理主要包括以下几个方面:

a. 缓存机制
Keep-Alive 组件内部维护了一个 cache 对象,用于存储它的子组件实例。当一个组件被首次渲染时,Keep-Alive 会将其缓存起来。之后如果需要再次渲染该组件,就会从缓存中取出并激活,而不是重新创建。
b. 生命周期钩子
Keep-Alive 引入了两个生命周期钩子:activated 和 deactivated。当组件被激活时,会调用 activated 钩子;当组件被停用时,会调用 deactivated 钩子。这允许开发者在组件被缓存和重新激活时执行特定的逻辑。
c. 渲染函数
Keep-Alive 组件没有模板,而是通过渲染函数来实现其功能。它的渲染函数会处理缓存逻辑,并返回需要渲染的 VNode。

二、深层作用

1. 缓存数据结构
在Vue3中, 使用以下数据结构管理缓存:
const cache = new Map(); const keys = [];
// 每个缓存项存储组件实例:
interface CacheItem { 
  component: ComponentPublicInstance; 
  key: string | number | symbol; 
  vnode: VNode; 
  // 其他内部属性... 
}

2. LRU(最近最少使用)算法实现
当组件数量超过 max 属性值时,Vue会移除最久未被访问的组件:
// 伪代码表示LRU逻辑 
function set(key, value) { 
  if (cache.has(key)) { 
    // 更新键的访问时间 
    cache.delete(key); 
    keys.delete(key); 
    } else if (cache.size >= maxCache) {
      // 移除最久未使用的项目 
      const oldestKey = keys[0]; cache.delete(oldestKey); 
      keys.shift(); 
    } 
  cache.set(key, value); 
  keys.push(key); 
}

3. 生命周期劫持与管理
keep-alive 会劫持组件的生命周期:
const KeepAlive = { 
  setup() { 
    const instance = getCurrentInstance(); 
    const cache = new Map(); 
    const keys = []; 
    // 在实例内部添加特殊生命周期钩子
    onMounted(() => { 
      activateChild(instance.subTree); 
    }); 
    onUpdated(() => { 
      activateChild(instance.subTree); 
    }); 
    // 核心激活逻辑 
    function activateChild(vnode) { 
      if (vnode.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) { 
        // 调用激活钩子 
        vnode.component!.activated && vnode.component!.activated(); 
      } 
    } 
  } 
}

4. 状态保留机制
通过缓存组件实例保留组件状态:
function pruneCacheEntry(cached) { 
  if (!current || cached.type !== current.type) {
     // 调用beforeUnmount钩子
    unmount(cached.component!); 
  } else if (current) { 
    // 重置缓存位置(LRU特性)
    const index = keys.indexOf(cached.key); 
    if (index > -1) { 
      keys.splice(index, 1); 
      keys.push(cached.key); 
    } 
  } 
}

5. 组件激活与停用过程

1.首次渲染​​:

  •  创建组件实例
  • 调用 onMounted 和 onActivated

 2. ​​组件切换​​:

  • 原组件调用 onDeactivated
  • 新组件调用 onActivated
  • 原组件DOM被移除但实例保留

3.​​缓存组件再次激活​​:

  • 直接从缓存恢复实例
  • 调用 onActivated (不调用 onCreated/onMounted)

4. ​​缓存淘汰​​:

  • 调用 onUnmounted
  • 释放组件实例和DOM资源
6. 可视化演示说明
在演示示例中可以看到:

1.​​状态保留​​:

  • 组件A/B输入框内容在缓存时会被保留
  • 组件C每次重新创建会重置内容

2.​​LRU缓存管理​​:

  • 当缓存数超过最大值时淘汰最旧缓存
  • 缓存信息区显示当前缓存状态

3.​​生命周期钩子​​:

  • 日志面板显示 activated/deactivated 调用
  • 缓存组件不触发 unmounted
7. 使用注意事项
最佳实践:

  

限制:
  • 不能直接缓存多个根节点组件(Vue3片段)
  • 被包含组件的name必须匹配
  • 频繁变动的组件不适合缓存

你可能感兴趣的:(javascript)