哈喽,我是前端小菜鸡。我今天带着不解的看了一下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这样的解释,保证唯一的集合顺序
那么是不是这样,比如我缓存组件中一开始缓存了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>