vue3使用keep-alive组件,包含动态组件使用详解
Vue3中keep-alive用于缓存组件状态并避免重复渲染。动态组件需使用markRaw或shallowRef避免响应式警告。keep-alive内不能有注释,否则报错。路由缓存需在component上使用v-if实现按需缓存,被缓存组件会触发activated/deactivated生命周期钩子。
vue3使用keep-alive组件,包含动态组件使用详解
在 Vue 3 项目中,keep-alive 几乎是缓存组件状态、避免重复渲染的标配方案。但很多开发者在实际使用中总会遇到一些奇怪的坑——比如动态组件缓存不生效、路由缓存莫名其妙地失效,或者报出“expects exactly one child component”这类让人摸不着头脑的错误。下面就从最基础的无缓存开始,一步步搭起完整的缓存体系,顺带把这些容易翻车的地方也一并点出来。
组件不使用keep-alive
先看一个最简单的例子:父组件通过 v-if/v-else 切换两个子组件,不添加任何缓存。
- Father
Fatherchange page
- Child
Child + {{ props.msg }}
- Child2
Child2 + {{ props.msg }}


每次点击按钮切换,对应的子组件都会被销毁并重新创建。如果子组件中有表单输入之类的状态,切换回来时数据就丢了。因此需要 keep-alive 来保持组件的“记忆”。
组件中使用
keep-alive 提供了两个常用的配置:
include:指定需要缓存的组件名(对应组件name选项)exclude:排除掉不需要缓存的组件
v-if切换
先看直接包裹 v-if/v-else 的情况:
这样两个组件都会被缓存。如果只想缓存其中的一个,可以利用 include:
此时只有 Child2 会被缓存,Child 每次切换都会重新创建。



component动态组件切换
因注释导致的意外错误
使用动态组件 component :is 时,经常会遇到一个报错:
expects exactly one child component

这个错误原因很简单——keep-alive 内部只能有一个直接子节点,而注释(HTML comment)会被当作一个虚拟节点,导致实际子节点变成了两个。比如下面这样就会报错:
解决方案有两种:要么用 动态组件的使用 如果直接用 [Vue warn]: Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be a voided by marking the component with 'markRaw' or using 'shallowRef' instead of 'ref'. 原因在于组件本身不应该被包装成响应式对象——只有组件的 props 和内部状态才需要响应。正确的做法是使用 完整示例 运行后可以看到,只有 路由场景下的缓存稍微复杂一些。先看没有缓存的写法: 和组件级一样,每次路由切换,页面组件都会被销毁重建,表单状态自然也就丢失了。 Vue Router 4(对应 Vue 3)引入了 在路由配置中,通过 这样,只有 被 利用这两个钩子,可以在组件被缓存或重新激活时做一些订阅/取消订阅、数据刷新等操作,非常实用。 以上就是 Vue 3 中使用
ref 或 reactive 来保存组件引用,Vue 会发出警告:
markRaw 或 shallowRef:
markRawconst com = ref(markRaw(Child2));const comChange = () => { if(com.value === Child2){ com.value = markRaw(Child); }else{ com.value = markRaw(Child2); }}
shallowRefconst com = shallowRef(Child2);const comChange = () => { if(com.value === Child2){ com.value = Child; }else{ com.value = Child2; }}
Child2 组件有缓存行为(生命周期中会触发 activated/deactivated),Child 每次切换都会经历销毁和重新创建。
路由不使用keep-alive
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'import Home from '@/views/Home.vue'const routes = [ { path: '/Father', name: 'Father', component: () => import('@/views/Father.vue'), children: [ { path: 'Child', name: 'Child', component: () => import('@/views/Child.vue'), }, { path: 'Child2', name: 'Child2', component: () => import('@/views/Child2.vue'), } ], }, ]const router = createRouter({ history: createWebHashHistory(), routes})export default router

路由中使用
的 v-slot API,可以方便地实现路由级别的缓存。这里有一个关键点:v-if 不能加在 keep-alive 上,否则直接销毁整个缓存容器;正确的做法是将 v-if 放在内部 component 上,实现按需缓存。
meta 属性标记哪些页面需要缓存:...const routes = [ { path: '/Father', name: 'Father', component: () => import('@/views/Father.vue'), children: [ { path: 'Child', name: 'Child', component: () => import('@/views/Child.vue'), }, { path: 'Child2', name: 'Child2', meta: { keepAlive: true, // 需要被keep-alive }, component: () => import('@/views/Child2.vue'), } ], }, ]...
keepAlive: true 的路由页面会被缓存,其他页面正常销毁。页面进入时触发的生命周期顺序为:created → mounted → activated。
keep-alive生命周期
keep-alive 包裹的组件会额外多出两个生命周期钩子,分别在 mounted 之后和 unMounted 之前触发:onActivated(() => { console.log('Component is activated')})onDeactivated(() => { console.log('Component is deactivated')})


总结
keep-alive 的完整实战指南,涵盖了组件内和路由层面的缓存方案,同时也指出了几个容易踩坑的地方——比如 keep-alive 内不能有注释、动态组件引用要用 markRaw 或 shallowRef、路由缓存的 v-if 要加在内部组件上等等。掌握这些细节后,缓存问题基本可以一次搞定,希望能在实际项目中帮上忙。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















