脚本专家 发表于 2026-6-11 20:00:05

Vue3中keep-alive组件缓存的正确用法:动态组件与路由缓存实践

在Vue3项目中,使用keep-alive组件可以缓存组件实例,避免重复渲染和状态丢失。本文通过代码示例详解Vue3中keep-alive在动态组件切换和路由缓存中的正确用法,包括常见错误及性能优化技巧。

一、基础用法:v-if切换组件缓存

如果不使用keep-alive,组件切换时会销毁和重建,表单输入等状态会丢失。以下示例中,Father组件通过v-if/v-else切换Child和Child2,每次切换都会重新挂载。


<!-- Father.vue -->
<template>
<div>
    <el-button @click="tabChange">change page</el-button>
    <Child :msg="cutTab" v-if="cutTab"></Child>
    <Child2 :msg="cutTab" v-else></Child2>
</div>
</template>
<script setup lang="ts" name="Father">
const cutTab = ref(false);
const tabChange = () => {
cutTab.value = !cutTab.value;
};
</script>


Child和Child2组件各包含一个input输入框,切换后输入内容会丢失。在keep-alive包裹后,组件实例会被缓存,切换回来时保留输入状态。

二、使用include/exclude控制缓存

keep-alive提供了include和exclude属性,用于指定需要缓存或排除的组件名称(对应组件name选项)。例如只缓存Child2:


<keep-alive include="Child2">
<Child :msg="cutTab" v-if="cutTab"></Child>
<Child2 :msg="cutTab" v-else></Child2>
</keep-alive>


三、动态组件与keep-alive的注意事项

当使用<component :is>动态切换时,keep-alive要求只有一个直接子节点。常见错误是在keep-alive内部放置注释或多根节点,导致控制台警告:"<KeepAlive> expects exactly one child component"。

错误示例:

<keep-alive include="Child2">
<!-- <Child :msg="cutTab" v-if="cutTab"></Child> -->
<component :is="com"></component>
</keep-alive>


注释被解析为子节点,导致keep-alive认为有多个子元素。正确做法是移除注释,仅保留一个动态组件。

四、动态组件引用时的性能优化

在Vue3中,直接用ref包裹组件会使其成为响应式对象,导致不必要的性能开销。Vue会尝试追踪组件对象的变化,而组件本身不需要响应式。

控制台警告:"Vue received a Component which was made a reactive object."

推荐使用markRaw或shallowRef来避免组件对象被响应式化。

使用markRaw:

const com = ref(markRaw(Child2));
const comChange = () => {
com.value = com.value === Child2 ? markRaw(Child) : markRaw(Child2);
};


使用shallowRef:

const com = shallowRef(Child2);
const comChange = () => {
com.value = com.value === Child2 ? Child : Child2;
};


完整示例(Father组件):

<template>
<div>
    <el-button @click="comChange">change component</el-button>
    <keep-alive include="Child2">
      <component :is="com"></component>
    </keep-alive>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from "@/views/Child.vue";
import Child2 from "@/views/Child2.vue";
const com = ref(markRaw(Child2));
const comChange = () => {
com.value = com.value === Child2 ? markRaw(Child) : markRaw(Child2);
};
</script>


效果:Child2被缓存(切换回来状态保留),Child每次销毁重建。

五、路由中使用keep-alive

Vue Router 4提供了v-slot API实现路由级缓存。通过meta属性标记需要缓存的页面。

布局组件示例:

<template>
<div>
    <router-view v-slot="{ Component }">
      <keep-alive>
      <component :is="Component" v-if="$route.meta.keepAlive" />
      </keep-alive>
      <component :is="Component" v-if="!$route.meta.keepAlive" />
    </router-view>
</div>
</template>


注意:v-if应加在<component>上而非keep-alive上,否则keep-alive会被直接销毁。

路由配置中设置meta:

{
path: 'Child2',
name: 'Child2',
meta: { keepAlive: true },
component: () => import('@/views/Child2.vue')
}


六、keep-alive生命周期

被keep-alive缓存的组件会额外触发两个生命周期钩子:onActivated和onDeactivated。它们在mounted之后、unMounted之前执行,分别对应组件被激活和失活时。


onActivated(() => {
console.log('Component is activated');
});
onDeactivated(() => {
console.log('Component is deactivated');
});


总结

合理使用keep-alive能显著提升Vue3应用的性能和用户体验。关键点:动态组件配合keep-alive时避免注释干扰;引用组件使用markRaw或shallowRef避免响应式开销;路由缓存通过meta配合v-slot实现;灵活运用include/exclude控制缓存范围。

热心网友6 发表于 2026-6-11 20:10:00

Re: Vue3中keep-alive组件缓存的正确用法:动态组件与路由缓存实践

楼主总结得很全面,特别是关于动态组件中组件对象被响应式化导致性能开销的提醒,这个坑很多人容易忽略。我补充一点实际开发中的经验:使用 `include` 或 `exclude` 时,组件必须显式设置 `name` 属性(在 `` 里可以用 `defineOptions` 定义),否则匹配不会生效,可能会导致缓存失败。另外在路由缓存场景下,如果路由较多,建议结合 `meta` 字段动态生成 `include` 数组,这样管理起来更灵活。感谢分享!

热心网友2 发表于 2026-6-20 10:05:00

Re: Vue3中keep-alive组件缓存的正确用法:动态组件与路由缓存实践

感谢分享!非常详细的实践总结,尤其`markRaw`和`shallowRef`避免组件对象响应式化的提醒很实用,之前确实没太注意这个性能问题。想追问一下,在路由缓存那部分,如果我想根据路由的`meta`动态控制哪些页面需要缓存,除了在`keep-alive`上写死`include`,有没有更灵活的做法?比如结合`computed`动态生成需要缓存的组件列表?

热心网友1 发表于 2026-6-20 12:30:00

Re: Vue3中keep-alive组件缓存的正确用法:动态组件与路由缓存实践

楼主的总结很清晰,尤其是**注释导致keep-alive警告**和**组件响应式化性能优化**这两点特别实用,我之前的项目里也踩过类似的坑。想请教一下:在路由keep-alive中,如果同时想根据meta动态控制哪些页面要缓存(比如include绑定一个计算属性),会不会遇到组件懒加载时名称未定义的问题?楼主有遇到过这种情况吗?
页: [1]
查看完整版本: Vue3中keep-alive组件缓存的正确用法:动态组件与路由缓存实践