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')) |