keep-alive
约 1498 字大约 5 分钟
2025-03-14
<keep-alive>
是 Vue 提供的一个内置的 缓存组件,作用于 动态组件 或 路由组件,避免它们在切换时被销毁并重新创建,从而保留组件的状态和 DOM 结构。以下是关于 keep-alive
的详细介绍:
一、核心功能
- 缓存组件实例:
当组件被包裹在<keep-alive>
中时,Vue 会将其缓存起来,而不是销毁。再次渲染时,直接从缓存中恢复,避免重新创建实例。 - 保留状态:
组件的状态(如数据、DOM 结构、滚动位置等)会被保留,提升用户体验。 - 优化性能:
避免重复渲染和初始化,减少性能开销。
二、基本用法
1、缓存动态组件
<template>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</template>
2、缓存路由组件
<template>
<keep-alive>
<router-view></router-view>
</keep-alive>
</template>
三、配置缓存规则
<keep-alive>
支持以下属性,用于控制缓存行为:
1、include
指定需要缓存的组件名(匹配组件的 name
选项)。
<keep-alive :include="['Home', 'Profile']">
<router-view></router-view>
</keep-alive>
<template>
<h2>组件</h2>
</template>
<script>
export default {
name: "Home", // 依赖 name 选项来确定是否缓存
};
</script>
2、exclude
指定不需要缓存的组件名。
<keep-alive :exclude="['Login', 'Register']">
<router-view></router-view>
</keep-alive>
3、max
指定最大缓存组件数。超出时,按 LRU(最近最少使用)算法淘汰最久未使用的缓存。
<keep-alive :max="5">
<router-view></router-view>
</keep-alive>
四、生命周期钩子
被缓存的组件会触发以下额外的生命周期钩子:
1、activated
触发时机:组件从缓存中恢复(激活)时调用。
用途:恢复数据、重新请求接口、启动定时器等。
export default { activated() { console.log("组件被激活"); }, };
2、deactivated
触发时机:组件被缓存(失活)时调用。
用途:清理副作用(如清除定时器、取消请求等)。
export default { deactivated() { console.log("组件被失活"); }, };
3、与普通组件的生命周期对比
初次加载时,被缓存的组件和普通组件要执行的钩子是一致的。
- 普通组件的生命周期里不会调用
activated
和deactivated
钩子,而是直接调用beforeDestroy
和destroyed
钩子直接销毁。 - 被缓存的组件不会被销毁,而是会在失活时触发
deactivated
钩子,在再次激活时触发activated
钩子。
生命周期钩子 | 普通组件 | 被缓存的组件 |
---|---|---|
beforeCreate | ✅ | ✅ |
created | ✅ | ✅ |
beforeMount | ✅ | ✅ |
mounted | ✅ | ✅ |
beforeUpdate | ✅ | ✅ |
updated | ✅ | ✅ |
beforeDestroy | ✅ | ❌(不会触发,改用 deactivated ) |
destroyed | ✅ | ❌(不会触发,改用 deactivated ) |
activated | ❌ | ✅ |
deactivated | ❌ | ✅ |
五、核心源码实现
methods: {
// LRU 缓存的实现方法
cacheVNode() {
const { cache, keys, vnodeToCache, keyToCache } = this
if (vnodeToCache) {
const { tag, componentInstance, componentOptions } = vnodeToCache
cache[keyToCache] = {
name: _getComponentName(componentOptions),
tag,
componentInstance
}
// 将 key 塞入 LRU 缓存队列
keys.push(keyToCache)
// 如果超过最大缓存数量,清理最旧的缓存
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
this.vnodeToCache = null
}
}
},
// 组件 created 钩子里初始化一些对象
created() {
this.cache = Object.create(null) // 初始化缓存
this.keys = [] // 初始化缓存键
this.vnodeToCache = null // 待缓存的虚拟节点
this.keyToCache = null // 待缓存的键
},
// 渲染函数
render() {
const slot = this.$slots.default
const vnode = getFirstComponentChild(slot)
const componentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// 获取组件 name
const name = _getComponentName(componentOptions)
const { include, exclude } = this
// 如果组件 name 不在包含列表中,或者在排除列表中,不缓存,直接返回
if (
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
return vnode
}
// 获取缓存、缓存键清单
const { cache, keys } = this
// 获取当前组件的 key,如果不存在就生成一个
const key =
vnode.key == null
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) {
// 命中缓存,直接返回组件实例 塞给 vnonde
vnode.componentInstance = cache[key].componentInstance
// 更新 LRU 缓存,将当前key移到最新位置
remove(keys, key)
keys.push(key)
} else {
// 缓存不存在,延迟设置缓存直到更新
// 组件执行完 render 后,一般继续执行beforeUpdate钩子、新旧虚拟 dom 的 diff(patch更新)、updated 钩子,
this.vnodeToCache = vnode
this.keyToCache = key
}
// 给组件添加标记
vnode.data.keepAlive = true
// 用于虚拟 dom 的 diff算法中,判断组件是不是缓存组件
// 如果是缓存组件,执行 deactivated 钩子
// 如果不是缓存组件,调用销毁钩子
// destroy(vnode: MountedComponentVNode) {
// const { componentInstance } = vnode
// if (!componentInstance._isDestroyed) {
// if (!vnode.data.keepAlive) {
// componentInstance.$destroy()
// } else {
// deactivateChildComponent(componentInstance, true /* direct */)
// }
// }
// }
}
return vnode || (slot && slot[0])
}
六、使用场景
1、缓存路由组件
在 Tab 切换或路由跳转时,保留组件的状态(如表单数据、滚动位置)。
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
2、缓存动态组件
在动态组件切换时,避免重复渲染和初始化。
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
3、优化性能
在需要频繁切换的组件中,减少性能开销。
<keep-alive :max="5">
<router-view></router-view>
</keep-alive>
七、注意事项
- 内存消耗:
缓存组件会占用内存,需合理设置max
属性,避免内存泄漏。 - 状态污染:
缓存可能导致状态残留,需在activated
中重置数据。 - 动态组件更新:
动态组件的key
变化会导致缓存失效,需确保key
稳定。 - 异步组件:
异步组件(如defineAsyncComponent
)也可以被缓存。
八、示例代码
1、缓存路由组件
// router.js
const routes = [
{
path: "/home",
component: Home,
meta: { keepAlive: true }, // 标记需要缓存的组件
},
{
path: "/detail",
component: Detail,
meta: { keepAlive: false },
},
];
<template>
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</template>
2、缓存动态组件
<template>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</template>
<script>
export default {
data() {
return {
currentComponent: "ComponentA",
};
},
};
</script>
九、总结
<keep-alive>
是 Vue 中用于缓存组件的重要工具,适用于以下场景:
- 保留组件状态:如表单数据、滚动位置。
- 优化性能:避免重复渲染和初始化。
- 动态组件切换:提升用户体验。
通过合理配置 include
、exclude
和 max
属性,可以更灵活地控制缓存行为,同时注意内存消耗和状态管理。
更新日志
2025/8/24 08:17
查看所有更新日志
e7112
-1于