keepalive组件不销毁组件是因为跳过销毁流程,将实例存入内存缓存池;activated和deactivated是缓存专属钩子,分别在组件激活和失活时触发,用于替代常规生命周期处理状态逻辑。

KeepAlive 里的组件不会销毁,是因为它根本没走销毁流程——Vue 把组件实例存进了内存缓存池,只是从 DOM 中暂时移除它的虚拟节点,而非调用 beforeDestroy 或 destroyed。真正起作用的,是 activated 和 deactivated 这两个“缓存专属钩子”,它们替代了常规生命周期,在组件进出视图时精准接管状态逻辑。
keep-alive 不是阻止销毁,而是跳过销毁
很多人误以为 keep-alive “阻止”了组件销毁,其实更准确的说法是:它让组件绕过了销毁路径。当一个被 keep-alive 包裹的组件切换离开时:
- Vue 不会触发
beforeDestroy、destroyed,也不会清空响应式数据或解绑事件 - 组件实例(包括 data、computed、watch、内部状态、甚至 input 的输入值、滚动位置)完整保留在内存中
- 对应的 vnode 被从真实 DOM 移除,但组件实例被存入 keep-alive 内部的
cache对象(一个 Map 或 Object),以组件 key 为索引 - 再次切换回来时,Vue 直接复用这个缓存实例,重新挂载 vnode,跳过
created→mounted全流程
activated 和 deactivated 是缓存场景下的“真·进入/离开钩子”
因为常规钩子在缓存复用时不执行,Vue 专门设计了 activated 和 deactivated 来填补这个空白。它们不是可选补充,而是缓存组件的“事实入口与出口”:
-
activated在组件从缓存中恢复并显示时触发 —— 适合刷新数据、恢复定时器、聚焦输入框、重置临时 UI 状态 -
deactivated在组件被隐藏、进入缓存时触发 —— 适合清理定时器、取消未完成请求、保存当前滚动位置、暂停视频或音频 - 这两个钩子每次组件显隐必触发(只要在 keep-alive 内),且只在缓存生效时才有意义;若组件未被缓存(比如被
exclude排除),它们根本不会执行
缓存控制决定钩子是否生效
keep-alive 的 include、exclude 和 max 属性,直接决定了哪些组件能进缓存池,进而决定 activated/deactivated 是否有机会运行:
- 只写
<keep-alive><router-view></router-view></keep-alive>:所有匹配路由组件默认缓存,钩子全量可用 - 加
:include="['UserList', 'UserProfile']":仅这两个组件享受缓存和对应钩子,其他路由组件仍走完整生命周期 - 加
:exclude="['Login', 'NotFound']":这些组件永远不缓存,activated/deactivated完全不触发 - 设
:max="5":缓存满后,最早缓存的组件会被pruneCacheEntry清出,此时会触发其deactivated,但不会触发destroyed(除非 keep-alive 自身被卸载)
典型使用场景与避坑提醒
这两个钩子不是“多写点就更健壮”,而是解决特定缓存副作用的关键工具:
- 需要每次打开都拉新数据?别再依赖
created,改用activated发请求 - 页面有轮播图或倒计时?在
activated启动,在deactivated清除setInterval,避免后台持续运行 - 表单页切走又切回,用户希望保留填写内容?不用额外处理 —— keep-alive 天然支持,但若用了
v-model.lazy或手动赋值,需确认数据源是否响应式同步 - 注意:不要在
deactivated里做异步操作(如 await API 调用),因为组件已脱离活跃状态,后续响应可能引发警告或内存泄漏










