vnode 实现动态组件切换与 keep-alive 缓存的核心在于手动控制创建/挂载/卸载及生命周期拦截;keep-alive 通过 deactivated/activated 钩子缓存并复用 vnode 与实例,不销毁 dom,仅移入/取出 cache map。

利用 VNode 实现动态组件切换,核心在于手动控制组件的创建、挂载与卸载时机;而 keep-alive 的缓存逻辑则建立在对 VNode 的生命周期拦截和缓存池管理之上——它不销毁被包裹组件的实例,而是将其 VNode 从真实 DOM 中移除后存入内存,并在再次激活时复用实例与 DOM 节点。
动态组件切换:基于 VNode 的手动控制
Vue 的 <component :is="comp"></component> 本质就是基于 VNode 的动态解析。你可以在渲染函数(render)中直接返回不同组件的 VNode,实现更精细的切换逻辑:
- 通过条件判断决定返回哪个组件的 VNode,例如
h(isActive ? Foo : Bar) - 可传入 props、slots、ref 等参数,完全掌控组件实例化过程:
h(Comp, { id: 123 }, { default: () => [h('p', 'content')] }) - 若需强制重置组件状态,可为 VNode 添加唯一
key,触发 Vue 的“新旧 VNode 对比”逻辑,使旧实例被销毁、新实例被创建
Keep-alive 如何缓存 VNode?关键在三个钩子
keep-alive 是一个内置抽象组件,自身不渲染真实 DOM,只负责管理其插槽中第一个子组件的 VNode 生命周期:
根据 package.json 中的 engines 字段自动检测 Node.js 版本需求,未安装 nvm 时自动安装并切换至对应版本。支持 Windows、macOS、Linux。适用于 Node.js 项目开发、版本切换和环境配置。
-
缓存时机:当子组件 VNode 的
componentInstance进入deactivated钩子时,keep-alive将该 VNode(连同其组件实例)存入cacheMap,但保留其 DOM 节点(不调用unmount) -
激活时机:再次渲染到同一组件时,
keep-alive从cache中取出原 VNode,跳过create阶段,直接复用组件实例,并触发activated钩子 -
剔除机制:当
max限制被突破,会按 LRU(最近最少使用)策略清除最早缓存的 VNode,调用其$destroy并移除 DOM 引用
手动模拟 keep-alive 行为(适合理解原理)
你可以用一个简单的渲染函数组件模拟缓存逻辑:
- 维护一个 Map:键为组件标识(如 name + key),值为缓存的 VNode
- 在
render中检查当前要渲染的组件是否已在缓存中;若有,直接返回缓存 VNode(注意:需确保 VNode 的key不变,否则 Vue 会认为是新节点) - 若无缓存,则调用
h()创建新 VNode,并在组件beforeUnmount时将其存入 Map(注意:此时 VNode 的 DOM 仍存在,可安全保留) - 需自行处理
activated/deactivated事件派发(通过onVnodeActivated/onVnodeDeactivated)
注意事项:VNode 缓存的边界与陷阱
不是所有 VNode 都适合或能被缓存:
- 函数式组件(无实例)无法被
keep-alive缓存,因为缺少组件实例来维持状态 - VNode 的
key必须稳定;若每次渲染都生成新key,缓存将失效 - 异步组件需确保加载完成后再进入缓存流程,否则可能缓存的是占位 VNode
- 手动操作 VNode(如 patch、clone)时,若未正确保留组件实例引用,会导致缓存失效或内存泄漏










