简单说一下vue中的keep-alive

54 阅读3分钟

哈喽,我是前端小菜鸡。我今天带着不解的看了一下keep-alive,keep-alive是怎么做到缓存?什么场景下去Keep-alive?keep-alive 组件个数有限制吗?如果keep-alive太多,会导致怎样? 好啦,好啦,直接进入主题。

这个是vue中的keep-alive中的github源码地址:core/packages/runtime-core/src/components/KeepAlive.ts at main · vuejs/core

在这个源码中,我们可以看到定义的几个变量 KeepAliveProps CacheKey, Keys

export interface KeepAliveProps {
  include?: MatchPattern
  exclude?: MatchPattern
  max?: number | string
}

type CacheKey = PropertyKey | ConcreteComponent
type Cache = Map<CacheKey, VNode>
type Keys = Set<CacheKey>

在keepAliveProps中定义的接口类型,我们可以看到,他有三个Keyof类型,include,exclude,max,
那这三个值有什么用呢?
我来解释一下:
include 就是在你的route路由表信息中,约定好了哪些是你要缓存的组件
exclude 就是跟Include反着来
而max:这个就是keep alive中允许规定缓存最大的值

接下来进入主要代码 为啥cache要用map,keys要用set呢?为啥要这样的设计呢? 翻开mdn,关于对set这样的解释,保证唯一的集合顺序 image.png

那么是不是这样,比如我缓存组件中一开始缓存了A,再缓存了B,再缓存了C,那么这样的顺序是不是ABC这样,那这样的设计可有大有来头,我们一开始不是看到了props中的定义了max最大的数量吗?那么vue中缓存组件当超过最大的数量的时候,他要怎么做呢?他是不是要去删除一些不常用的缓存组件,那么vue是怎么知道哪些缓存组件不常用?这个set就是可以很好的解释了这一点,是不是只要是第一个的,那么就是不经常用的,那么我去找到他并且删除他,动态的这样做,是不是就可以永远维持到这个最大数,废话不多说 上代码

const comp = vnode.type as ConcreteComponent
const key = vnode.key == null ? comp : vnode.key
pendingCacheKey = key
const cachedVNode = cache.get(key)

const cache: Cache = new Map()
const keys: Keys = new Set()
let current: VNode | null = null

// tips: keys 仅仅是用于收集组件的 key,组件实际收集在 cache 中
if (cachedVNode) { // 节点存在缓存中
  vnode.el = cachedVNode.el
  vnode.component = cachedVNode.component

  // 将当前组件节点放到队尾
  keys.delete(key) 
  keys.add(key)
} else { // 不存在
  keys.add(key) // 加入缓存队列
  // prune oldest entry
  // 超过最大值了,将最久没有使用的组件删除,也就是队头组件
  if (max && keys.size > parseInt(max as string, 10)) { 
    pruneCacheEntry(keys.values().next().value)
  }
}
       

keep-alive 如何做到缓存

1: 获取map缓存

vnode.componentInstance = cachedVNode.componentInstance;
如果有,则直接复用并更新key值,没有的话,就直接渲染,并缓存起来

2:获取set中的key值,对key值的逻辑处理

if (cachedVNode) {
  keys.delete(key); // 从 Set 中移除
  keys.add(key);    // 重新添加到末尾(表示最近使用)
} else {
      keys.add(key);    // 新缓存
          if (max && keys.size > max) {
pruneCacheEntry(keys.values().next().value); // 淘汰最久未使用的缓存
      }
}

3:取出map缓存的组件 - 如果存在,直接复用缓存的 VNode,并更新 keys(标记为最近使用)。 - 如果不存在,渲染新组件,并存入缓存。 4:在onMounted和onUpdated中缓存最新的组件

// 核心代码如下
const cache: Cache = new Map() // 获取缓存map结构
const keys: Keys = new Set()  // 获取缓存的key set集合
// cache sub tree after render 
let pendingCacheKey: CacheKey | null = null
const cacheSubtree = () => {
  // fix #1621, the pendingCacheKey could be 0
  if (pendingCacheKey != null) {
    cache.set(pendingCacheKey, getInnerChild(instance.subTree))
  }
}
// 组件挂载和更新的时候就会缓存最新的组件
onMounted(cacheSubtree)
onUpdated(cacheSubtree)

keep-alive 在哪个生命周期激活 activate 中去激活,activate 就相当于mounted生命周期。

keep-alive 缓存太多,会导致什么问题? 内存泄漏

keep-alive 应用场景 1:如果你希望用户保留上一次的行为,那就用。

keep-alive的常见写法

<RouterView v-slot="{ Component }"> <KeepAlive :include="cachedViews"> <component :is="Component" :key="$route.fullPath" /> </KeepAlive> </RouterView>