六维教程

Vue3 动态组件与缓存

页面上经常有”同一位置、显示不同组件”的需求:Tab 切来切去、步骤条上下一步、侧边栏图标区切换视图。

Vue 用 <component :is="..."> 支持动态切换,再配合 <KeepAlive> 缓存切换前的状态。

动态组件 <component :is>

把要渲染的组件对象交给 :is,它就渲染成对应组件。切换 :is 的值,组件随之替换:

<!-- App.vue -->
<script setup>
import { ref } from 'vue'
import TabHome from './components/TabHome.vue'
import TabList from './components/TabList.vue'
import TabMine from './components/TabMine.vue'

// 当前要显示的组件(存的是组件对象本身)
const currentTab = ref(TabHome)
</script>

<template>
  <button @click="currentTab = TabHome">首页</button>
  <button @click="currentTab = TabList">列表</button>
  <button @click="currentTab = TabMine">我的</button>

  <!-- 根据 currentTab 的值渲染对应组件 -->
  <component :is="currentTab" />
</template>

缓存组件状态 <KeepAlive>

<component :is> 切换组件时,旧组件会被卸载,它里面的输入框内容、滚动位置、计时器状态全部归零,切回来 = 重新创建,之前填的内容就丢了。

比如 Tab 里有表单,用户填了一半切走再切回,填的内容全没了,体验很差,这时就要用 <KeepAlive>

把动态组件包进 <KeepAlive>,被切换出去的组件不会被销毁,而是缓存起来,切回来时保留原样:

<template>
  <KeepAlive>
    <component :is="currentTab" />
  </KeepAlive>
</template>

实测效果:在某个 Tab 里输入文字,切到别的 Tab 再切回来,文字还在,因为组件实例被缓存,没有重新创建。

include / exclude

<KeepAlive> 默认缓存所有切换过的组件,只想缓存其中一部分时,用 include 指定缓存名单,名单外的组件切换出去照常销毁:

<!-- 只缓存名单里的 TabHome 和 TabList -->
<KeepAlive :include="['TabHome', 'TabList']">
  <component :is="currentTab" />
</KeepAlive>

不想缓存某些组件时,用 exclude 指定排除名单,名单外的全部缓存:

<!-- 除 TabMine 外,其他组件都缓存 -->
<KeepAlive :exclude="['TabMine']">
  <component :is="currentTab" />
</KeepAlive>

exclude 排除的组件切换出去时照常销毁,切回来重新创建,上次的内容不会保留。

两个属性可以同时使用,组件名同时命中两者时,以 exclude 为准。

属性值写组件的 name<script setup> 写法下组件名默认等于文件名(TabHome.vue 对应 TabHome)。

生命周期

被缓存组件切换出去不再走 onUnmounted(它没被卸载),而是触发 onDeactivated(停用),切回来触发 onActivated(再次激活)。

之前生命周期篇提到的这对钩子,就是为 KeepAlive 准备的:

<script setup>
import { onActivated, onDeactivated } from 'vue'

onActivated(() => {
  // 从缓存重新显示时执行:刷新列表、恢复计时等
})
onDeactivated(() => {
  // 被缓存隐藏时执行:暂停计时、取消轮询等
})
</script>

异步组件

defineAsyncComponent 可以把组件按需加载,用到才加载代码,减少首屏体积,项目大了以后很实用,现在了解即可:

import { defineAsyncComponent } from 'vue'

const BigModal = defineAsyncComponent(() => import('./components/BigModal.vue'))

配合动态组件使用:<component :is="BigModal" />,真正优化时再研究它,不用担心现在看不懂。

速查卡片

场景 写法
动态切换组件 <component :is="currentComponent" />
切换保留状态 <KeepAlive> 包住动态组件
只缓存部分组件 <KeepAlive :include="['名字']">
激活/停用钩子 onActivated / onDeactivated
异步加载组件 defineAsyncComponent(() => import('./X.vue'))
上一篇
Vue3 依赖注入
下一篇
Vue3 数据请求