动态组件:component
有些场景会需要在多个组件间来回切换,比如 Tab 界面:
那么使用动态组件无疑是个好的解决方案。
先把几个tab组件搞出来,代码都非常简单。
html
// ------------------- src/components/A.vue -------------------
<template>
<div>A组件</div>
</template>
<script>
</script>
<style scoped>
</style>
// ------------------- src/components/B.vue -------------------
<template>
<div>B组件</div>
<input type="text">
</template>
<script>
</script>
<style scoped>
</style>
// ------------------- src/components/C.vue -------------------
<template>
<div>C组件</div>
</template>
<script>
</script>
<style scoped>
</style>
然后其他文件不变,这里主要在App.vue
中做演示:
html
<template>
<div>
<h1>动态组件</h1>
<div>
<button v-for="(item, index) in tableList" @click="change(index)">{{item.title}}</button>
</div>
<!-- 动态组件使用标签component,其is的值是组件名,对应的组件就会渲染在这里 -->
<!-- 下面为了演示方便我做了个数据双向绑定 -->
<component :is="currentComponent.com"></component>
</div>
</template>
<script setup>
import A from './components/A.vue'
import B from './components/B.vue'
import C from './components/C.vue'
import {reactive, markRaw} from "vue";
// 关于markRaw,标记一个对象,使其永远不会转换为 proxy。返回对象本身。意思就是使其不能成为一个响应式对象。
// 跳过 proxy 转换可以提高性能。
const tableList = reactive([
{title: "A组件", com: markRaw(A)},
{title: "B组件", com: markRaw(B)},
{title: "C组件", com: markRaw(C)},
])
const currentComponent = reactive({
com:tableList[0].com
})
function change(index) {
currentComponent.com = tableList[index].com
}
</script>
<style scoped>
</style>
上面代码运行后,就是点击对应的按钮,显示对应的组件,切换非常流畅。
默认情况下,一个组件实例在被替换掉后会被销毁。这会导致它丢失其中所有已变化的状态——当这个组件再一次被显示时,会创建一个只带有初始状态的新实例。
例如你在B组件中的input框中输入数据的话,你会发现再次切换之后,这个输入的数据就消失了。这是因为每当切换组件时,上一个组件就会销毁,等下次切换时再加载,这就导致了之前的组件销毁时,其中input框的数据也就没了....
如果要保留输入的值,其实也有招,那就是切换时将组件缓存下来,不让其组件销毁就行了。这就用到了keep-alive组件了。
keep-alive
<KeepAlive>
是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。
html
<template>
<div>
<h1>动态组件</h1>
<div>
<button v-for="(item, index) in tableList" @click="change(index)">{{item.title}}</button>
</div>
<!-- 通过keep-alive包裹的组件将会被缓存,且默认会缓存内部的所有组件实例 -->
<keep-alive>
<component :is="currentComponent.com"></component>
</keep-alive>
<!-- 我们可以通过 include 和 exclude prop 来定制该行为。这两个 prop 的值都可以是一个以英文逗号分隔的字符串、一个正则表达式,或是包含这两种类型的一个数组 -->
<!-- 以英文逗号分隔的字符串 -->
<keep-alive include="a,c">
<component :is="currentComponent.com"></component>
</keep-alive>
<!-- 正则表达式 (需使用 `v-bind`) -->
<keep-alive :include="/a|c/">
<component :is="currentComponent.com"></component>
</keep-alive>
<!-- 数组 (需使用 `v-bind`) -->
<keep-alive :include="['a', 'c']">
<component :is="currentComponent.com"></component>
</keep-alive>
</div>
</template>
<script setup>
import A from './components/A.vue'
import B from './components/B.vue'
import C from './components/C.vue'
import {reactive, markRaw} from "vue";
// 关于markRaw,标记一个对象,使其永远不会转换为 proxy。返回对象本身。意思就是使其不能成为一个响应式对象。
// 跳过 proxy 转换可以提高性能。
const tableList = reactive([
{title: "A组件", com: markRaw(A)},
{title: "B组件", com: markRaw(B)},
{title: "C组件", com: markRaw(C)},
])
const currentComponent = reactive({
com:tableList[0].com
})
function change(index) {
currentComponent.com = tableList[index].com
}
</script>
<style scoped>
</style>