查看: 92|回复: 1

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

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
在Vue3项目中,使用keep-alive组件可以缓存组件实例,避免重复渲染和状态丢失。本文通过代码示例详解Vue3中keep-alive在动态组件切换和路由缓存中的正确用法,包括常见错误及性能优化技巧。

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

如果不使用keep-alive,组件切换时会销毁和重建,表单输入等状态会丢失。以下示例中,Father组件通过v-if/v-else切换Child和Child2,每次切换都会重新挂载。
  1. <!-- Father.vue -->
  2. <template>
  3.   <div>
  4.     <el-button @click="tabChange">change page</el-button>
  5.     <Child :msg="cutTab" v-if="cutTab"></Child>
  6.     <Child2 :msg="cutTab" v-else></Child2>
  7.   </div>
  8. </template>
  9. <script setup lang="ts" name="Father">
  10. const cutTab = ref(false);
  11. const tabChange = () => {
  12.   cutTab.value = !cutTab.value;
  13. };
  14. </script>
复制代码

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

二、使用include/exclude控制缓存

keep-alive提供了include和exclude属性,用于指定需要缓存或排除的组件名称(对应组件name选项)。例如只缓存Child2:
  1. <keep-alive include="Child2">
  2.   <Child :msg="cutTab" v-if="cutTab"></Child>
  3.   <Child2 :msg="cutTab" v-else></Child2>
  4. </keep-alive>
复制代码

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

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

错误示例:
  1. <keep-alive include="Child2">
  2.   <!-- <Child :msg="cutTab" v-if="cutTab"></Child> -->
  3.   <component :is="com"></component>
  4. </keep-alive>
复制代码

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

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

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

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

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

使用markRaw:
  1. const com = ref(markRaw(Child2));
  2. const comChange = () => {
  3.   com.value = com.value === Child2 ? markRaw(Child) : markRaw(Child2);
  4. };
复制代码

使用shallowRef:
  1. const com = shallowRef(Child2);
  2. const comChange = () => {
  3.   com.value = com.value === Child2 ? Child : Child2;
  4. };
复制代码

完整示例(Father组件):
  1. <template>
  2.   <div>
  3.     <el-button @click="comChange">change component</el-button>
  4.     <keep-alive include="Child2">
  5.       <component :is="com"></component>
  6.     </keep-alive>
  7.   </div>
  8. </template>
  9. <script setup lang="ts" name="Father">
  10. import Child from "@/views/Child.vue";
  11. import Child2 from "@/views/Child2.vue";
  12. const com = ref(markRaw(Child2));
  13. const comChange = () => {
  14.   com.value = com.value === Child2 ? markRaw(Child) : markRaw(Child2);
  15. };
  16. </script>
复制代码

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

五、路由中使用keep-alive

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

布局组件示例:
  1. <template>
  2.   <div>
  3.     <router-view v-slot="{ Component }">
  4.       <keep-alive>
  5.         <component :is="Component" v-if="$route.meta.keepAlive" />
  6.       </keep-alive>
  7.       <component :is="Component" v-if="!$route.meta.keepAlive" />
  8.     </router-view>
  9.   </div>
  10. </template>
复制代码

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

路由配置中设置meta:
  1. {
  2.   path: 'Child2',
  3.   name: 'Child2',
  4.   meta: { keepAlive: true },
  5.   component: () => import('@/views/Child2.vue')
  6. }
复制代码

六、keep-alive生命周期

被keep-alive缓存的组件会额外触发两个生命周期钩子:onActivated和onDeactivated。它们在mounted之后、unMounted之前执行,分别对应组件被激活和失活时。
  1. onActivated(() => {
  2.   console.log('Component is activated');
  3. });
  4. onDeactivated(() => {
  5.   console.log('Component is deactivated');
  6. });
复制代码

总结

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

使用道具 举报

发表于 2 小时前 | 显示全部楼层

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

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

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-11 23:07 , Processed in 0.057036 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部