keep-alive 组件原理
<keep-alive> 是 Vue 面试里的高频题,因为它同时涉及:
- 组件缓存
- 生命周期
- 路由切换
- 性能优化
如果只回答“它可以缓存组件,避免重复渲染”,只能算入门。更完整的回答应该是:
keep-alive不是真的把组件“留在 DOM 上不动”,而是 缓存组件实例和子树,在切换时走激活/失活,而不是重新挂载/卸载。
面试速答(30 秒版 TL;DR)
keep-alive是一个抽象组件,本身不渲染真实 DOM。- 它会缓存被包裹组件的 VNode / 组件实例 / 子树状态。
- 组件切走时不是卸载,而是 deactivated;切回来时不是重新创建,而是 activated。
- 可通过
include、exclude、max控制缓存范围和数量。 max本质上是一个近似 LRU 的缓存淘汰策略。
1. keep-alive 是什么?
它是一个内置抽象组件,常见写法:
<keep-alive>
<router-view />
</keep-alive>
“抽象组件”意味着:
- 它主要参与组件调度,不生成自己的真实 DOM
- 它关注的是“如何缓存和复用子组件”
2. 它缓存的到底是什么?
不是单纯缓存一份 HTML,而是缓存:
- 组件实例
- 渲染出的子树 VNode
- 组件内部响应式状态
- DOM 关联关系
所以切回来时,用户之前输入过的表单、滚动位置、局部状态,常常还能保住。
3. 核心机制:挂载一次,后续激活/失活
正常组件切换:
- 离开时卸载
- 回来时重新创建实例并重新挂载
被 keep-alive 包裹后:
- 首次进入:正常挂载
- 离开:不卸载,而是失活
- 再回来:不重新创建,而是激活
所以对应的生命周期会变成:
- 首次进入:
mounted+activated - 切走:
deactivated - 切回:
activated - 真正被移出缓存:才走
beforeUnmount/unmounted
4. 它是怎么找到“同一个组件”的?
缓存命中的关键,一般基于:
- 组件类型
key
如果是同一路由组件,但你给了不同的 key,那 Vue 会认为它们是不同缓存项。
例如:
<router-view :key="$route.fullPath" />
这会导致参数变化时更容易重新创建,而不是复用原缓存。
5. include / exclude / max 的作用
5.1 include
只缓存命中的组件名。
<keep-alive :include="['UserList', 'OrderList']">
<router-view />
</keep-alive>
5.2 exclude
排除不需要缓存的组件名。
5.3 max
限制缓存数量。超过上限后,会淘汰最久未使用的缓存项。
这个思路本质上接近 LRU:
- 最近访问过的,保留
- 很久没访问的,优先淘汰
6. 一个简化版心智模型
可以把 keep-alive 想成一个 Map:
const cache = new Map()
function render(vnode) {
const key = vnode.key ?? vnode.type
if (cache.has(key)) {
vnode.component = cache.get(key).component
activate(vnode)
} else {
mount(vnode)
cache.set(key, vnode)
}
}
真实实现比这复杂得多,但核心思想就是:
- 命中缓存就复用
- 没命中就首次挂载并记录
7. 为什么 keep-alive 常和 router-view 一起用?
因为列表页、表单页、详情页切换时,经常希望保留:
- 列表筛选条件
- 滚动位置
- 已输入但未提交的表单内容
如果每次都卸载重建,用户体验会很差。
这是它最典型的落地场景。
8. 常见问题
8.1 为什么切换回来不触发 mounted?
因为组件根本没被重新挂载,只是从“失活”切回“激活”。
这时你应该把“每次回来都要执行”的逻辑写在:
- Vue 2:
activated - Vue 3:
onActivated
8.2 为什么有时看起来还是重新渲染了?
常见原因有:
include/exclude不匹配- 组件名不符合预期
key变了max触发了淘汰
8.3 keep-alive 能解决所有性能问题吗?
不能。它适合“重复切换、状态值得保留”的组件。若缓存的是:
- 超大列表
- 大量复杂图表
- 占内存很重的页面
反而可能把内存顶高。
9. 面试高频答法
Q1:keep-alive 为什么不触发销毁钩子?
答:因为它不是让组件真正卸载,而是把组件从活跃树中移开并缓存起来。组件进入的是失活状态,对应 deactivated,而不是销毁状态。
Q2:keep-alive 的 max 是怎么工作的?
答:本质上是限制缓存条目数,超过后按近似 LRU 的策略淘汰最久未访问的缓存项。被淘汰的组件才会真正卸载。
Q3:什么时候不建议用 keep-alive?
答:页面状态不值得保留、组件内存占用很大、切回来必须强制重新拉全量数据时,不一定适合用。
速记要点
keep-alive缓存的是实例和子树,不是静态 HTML- 首次挂载,后续是
activated/deactivated - 命中缓存看组件类型和
key include/exclude/max控制缓存范围和淘汰