Vue3 之 动态组件和KeepAlive组件

一、动态组件

1、简介

​ 在某些业务场景下,页面的某模块具有多个组件但在同一时间只显示一个,需要在多个组件之间进行频繁的切换,如:tab切换等场景。除了可以使用v-ifv-show根据不同条件显示不同组件之外,还可以通过动态组件<component>来实现相同的效果。<component>虽然被称为动态组件,但其并非是内置组件,而是属于模板语法,在模板编译阶段会被编译。

​ 动态组件允许在同一挂载节点动态切换多个组件,可以根据具体条件,动态决定显示的组件。比起v-if/v-show的实现方式来说,无需创建多个挂载节点,且代码量更少。

​ 动态组件默认只保持当前组件存活,其余被切换掉的组件会被卸载,但可以结合<KeepAlive>组件实现被切换掉的组件保持存活状态。

2、基础用法

​ 动态组件的核心在于<component>标签和is属性,Vue会根据is属性的值来决定具体渲染在<component>标签位置上的是哪个组件。

<component :is="son1"></component>

​ 在通过is属性指定展示的子组件时,is属性的值可以是组件在引入到当前组件时定义的注册名称(String类型,常在选项式API中使用),也可以是组件本身的定义(Component类型,常在组合式API中使用)。

组合式API中使用组件本身的定义决定渲染组件:

​ 在组合式API中,如果我们需要使用变量存储导入的子组件实例,如果使用ref,则控制台会抛出warn。因为ref是将组件实例转换为响应式对象,可能会导致不必要的性能开销,建议使用markRaw(对象本身)或shallowRef(浅层响应式)来避免这种情况。

<template><div><h1>这就是动态组件</h1><!-- 根据按钮点击切换组件 --><button v-for="(item, index) in components" :key="index" @click="currentComponent = item">{{ index }}</button><!-- 使用动态组件 --><component :is="currentComponent" /></div>
</template><script setup lang="ts">
import { shallowRef } from 'vue'
// 导入子组件
import son1 from '../components/dtzj-son1.vue'
import son2 from '../components/dtzj-son2.vue'
import son3 from '../components/dtzj-son3.vue'// 定义当前展示的子组件 值为对子组件的引用
const currentComponent = shallowRef(son1)
// 定义子组件对象
const components = {son1,son2,son3
}// 2秒后切换显示的子组件
setTimeout(() => {currentComponent.value = son3
}, 2000)
</script>
选项式API中使用组件注册名决定渲染组件:
<template><component :is="view" />
</template><script>
import Foo from './Foo.vue'
import Bar from './Bar.vue'export default {components: { Foo, Bar },data() {return {view: 'Foo'}}
}
</script>
3、渲染普通HTML元素

is属性的值还可以是普通的HTML标签名(不包含<>),但是要以字符串的形式设置。Vue会根据字符串的值渲染对应的HTML标签。<component>标签可以写成双标签的形式,内部包含其他内容。

<template><div><!-- 渲染普通HTML --><component v-for="(item, index) in htmlList" :key="index" :is="item">{{ item+'标签' }}</component></div>
</template><script setup lang="ts">
import { ref } from 'vue'// 定义要渲染的html标签
const htmlList = ref(['a','span','div','h5','p','i','aside'])
</script>
页面效果:

在这里插入图片描述

4、渲染内置组件

​ 动态组件还可以将内置组件(<Transition>Teleport等)作为要渲染的内容,但实际上这种场景并不多见,因此就不展开叙述了。

<template><div><!-- 渲染内置组件 --><component :is="Transition"><div v-show="show">这里是需要过渡的内容</div></component><!-- 等同于 --><Transition><div v-show="show">这里是需要过渡的内容</div></Transition></div>
</template><script setup lang="ts">
// 导入内置组件
import { ref, Transition } from 'vue'
// 定义div的显/隐
const show = ref(false)// 3秒后切换div的显隐状态
setTimeout(() => {show.value = true
}, 3000)</script><style scoped>
/* 定义过渡样式 */
.v-enter-active,
.v-leave-active {transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {opacity: 0;
}
</style>
5、v-model的特殊性

​ 在<component> 标签上使用 v-model时,模板编辑器会将其扩展为modelValue的prop和update:modelValue的事件监听器,而并非原始的v-model双向绑定功能。

​ 如果渲染的普通自定义子组件,内部可以接收prop和使用事件监听器,或者使用defineModel()宏方法,进行相应操作。

父组件:
<template><div><!-- 使用的动态组件 并使用v-mode --><component :is="currentComponent" v-model="test" /><h3>{{ test }}</h3></div>
</template><script setup lang="ts">
import { shallowRef } from 'vue'
// 导入子组件
import son1 from '../components/dtzj-son1.vue'// 定义当前展示的子组件 值为对子组件的引用
const currentComponent = shallowRef(son1)
// 定义一个变量
const test = ref('test')
</script>
子组件:
<template><div><h5>{{ modelVar }}</h5></div>
</template><script setup lang="ts">
import { onMounted } from 'vue'const  modelVar = defineModel();onMounted(() => {setTimeout(() => {modelVar.value = '子组件更改了modelVar';}, 1000)
})
</script>

​ 但如果渲染的是普通HTML元素,且是inputtextareaselect等本身可以使用v-model的元素,则v-model的双向绑定功能不会起作用。如果需要实现双向绑定,则可以手动通过对应的attribute和事件来实现。

<script setup>
import { ref } from 'vue'const tag = ref('input')
const username = ref('')
</script><template><!-- 由于 'input' 是原生 HTML 元素,因此这个 v-model 不起作用 --><component :is="tag" v-model="username" />
</template>

二、KeepAlive

1、简介

​ 在上面的内容中,讲解了动态组件的相关知识,但是在默认情况下,通过动态组件被切走的组件,会被卸载后被销毁,其内部的所有变动过的状态会丢失,等再次切换回该组件时,则会重新创建该组件的组件实例。但在某些场景下,我们不希望被切走的组件被销毁,并且保留其内部状态,那此时就需要借助KeepAlive内置组件。

​ 将KeepAlive组件包裹在动态组件的外层,当动态组件发生切换时,默认会将所有被切走的非活跃组件进行缓存,而不是销毁,并且组件内部的状态也会被保留。

​ 在需要频繁反复切换动态组件的业务场景中,如:tab切换、路由转换等,使用KeepAlive组件可以减少组件实例的销毁和创建过程,从而优化页面的性能。

2、基础用法

KeepAlive组件内部可以包裹动态组件,也可以包裹普通组件。但KeepAlive组件在任何时间节点,只能有一个活跃组件作为其直接子节点,不允许多个组件共存作为直接子节点。

动态组件:

​ 当KeepAlive组件内部包裹动态组件时,如果动态组件发生的切换,那被切走的组件默认会被缓存,当再次切换回该组件时,将从缓存中将组件取出,重新显示。

<template><div><KeepAlive><component :is="currentComponent" /></KeepAlive></div>
</template><script setup lang="ts">
import { shallowRef, KeepAlive } from 'vue'
import son1 from '../components/keepalive-son1.vue'
import son2 from '../components/keepalive-son2.vue'// 定义当前展示的子组件 值为对子组件的引用
const currentComponent = shallowRef(son1)// 3秒切换显示的子组件
setTimeout(() => {currentComponent.value = son2
}, 3000)
</script>
普通组件:

​ 当KeepAlive组件内部包裹普通组件时,通常与v-if/v-else-if/v-else指令结合使用,保证组件内部在同一时间节点只能有一个组件作为直接子节点。

<template><div><KeepAlive><son1 v-if="show === 1"></son1><son2 v-else></son2></KeepAlive></div>
</template><script setup lang="ts">
import { ref, KeepAlive } from 'vue'
import son1 from '../components/keepalive-son1.vue'
import son2 from '../components/keepalive-son2.vue'// 决定显示的组件
const show = ref(1)// 3秒后切换显示的子组件
setTimeout(() => {// currentComponent.value = son2show.value = 2;
}, 3000)
</script>

注意: 不能使用v-show指令,因为其仅仅是通过设置display属性实现的元素显隐,其节点依旧保留在DOM中,实际上会让KeepAlive组件内部同时存在多个直接子节点,从而引发报错。

3、组件属性

KeepAlive组件有三个可以指定的属性,分别为:includeexcludemax

interface KeepAliveProps {/*** 如果指定,则只有与 `include` 名称* 匹配的组件才会被缓存。*/include?: MatchPattern/*** 任何名称与 `exclude`* 匹配的组件都不会被缓存。*/exclude?: MatchPattern/*** 最多可以缓存多少组件实例。*/max?: number | string
}type MatchPattern = string | RegExp | (string | RegExp)[]
include:

KeepAlive组件默认会缓存内部所有非活跃组件实例,但缓存过多的组件实例也会占用过多的内存资源,因此可以通过include属性显式的指定要被缓存的组件,未被指定的组件则不会被缓存。

include属性的值可以是英文逗号分割的字符串,或者一个正则表达式,以及包含两种类型的数组。如果属性值为后两者,则需要使用v-bind进行绑定。

<!-- 属性值为字符串 如果要缓存多个组件 需要以英文逗号分割的 注意分隔符前后不加空格 -->
<KeepAlive include="keepalive-son1,keepalive-son2"><component :is="currentComponent" />
</KeepAlive>
<!--属性值为数组形式 指定多个组件 -->
<KeepAlive :include="['keepalive-son1', 'keepalive-son2']"><component :is="currentComponent" />
</KeepAlive>
<!-- 属性值为正则表达式 符合匹配条件的组件会被缓存 -->
<KeepAlive :include="/^keepalive-son/"><component :is="currentComponent" />
</KeepAlive><script setup lang="ts">
import { ref, shallowRef, KeepAlive } from 'vue'
// 注意这里的 son1 是在当前组件内的注册名称 组件本身生成的name属性为 keepalive-son1
import son1 from '../components/keepalive-son1.vue'
import son2 from '../components/keepalive-son2.vue'// 定义当前展示的子组件 值为对子组件的引用
const currentComponent = shallowRef(son1)// 3秒后切换显示的子组件
setInterval(() => {currentComponent.value === son1? (currentComponent.value = son2): currentComponent.value = son1
}, 3000)
</script>

​ 该属性的属性值会与子组件的name选项进行匹配,在选项式API中必须显示的声明name选项,在组合式API中使用<script setup>的组件会根据文件名称隐式的生成完全相同的name选项,无需手动声明。

使用<script setup>的组件keepalive-son1.vue

<template><div><p>这是子组件1中的count:{{ count }}</p><button @click="count++">add</button></div>
</template><script setup lang="ts">
import { ref, onMounted } from 'vue'
const count = ref(1111)// 此时组件没有显式指定name属性 则会隐式的设置name的值为 keepalive-son1
// KeepAlive 会根据 keepalive-son1 进行识别
</script>

非使用<script setup>的组件keepalive-son2.vue

<template><div><p>这是子组件2中的count:{{ count }}</p><button @click="count++">add</button></div>
</template><script>
export default {// 显式的指定name属性name: 'KeepaliveSon2',data() {return {count: 2222};},};
</script>

​ 如果在使用<script setup>的单文件组件中,想要显式的指定组件的name属性,可以通过defineOptions()宏方法或者export default语法来指定:

<!-- 方法一: 通过新的script + export default 定义组件的name属性 -->
<!-- 注意两个 script 的 lang 属性要一致 -->
<script lang="ts">
export default {name: 'KeepaliveSon1',
}
</script><script setup lang="ts">
import { ref, onMounted } from 'vue'const count = ref(1111)
// 方法二: 通过 defineOptions 定义组件的name属性
defineOptions({name: 'KeepaliveSon1',
})
</script>
exclude:

exclude属性与include属性正好相反,用于指定哪些组件不会被缓存。属性值类型也相同,可以是英文逗号分割的字符串,或者一个正则表达式,以及包含两种类型的数组。

<!-- 属性值为字符串 如果要指定不缓存多个组件 需要以英文逗号分割的 注意分隔符前后不加空格 -->
<KeepAlive exclude="KeepaliveSon1,KeepaliveSon2"><component :is="currentComponent" />
</KeepAlive>
<!--属性值为数组形式 指定多个组件不缓存 -->
<KeepAlive :exclude="['keepalive-son1', 'keepalive-son2']"><component :is="currentComponent" />
</KeepAlive>
<!-- 属性值为正则表达式 符合匹配条件的组件不会被缓存 -->
<KeepAlive :exclude="/^keepalive-son/"><component :is="currentComponent" />
</KeepAlive>

​ 属性值会与子组件的name选项进行匹配,在选项式API中必须显示的声明name选项,在组合式API中使用<script setup>的组件会根据文件名称隐式的生成完全相同的name选项,无需手动声明。如果想要显式的指定name属性,可以通过defineOptions()宏方法或者export default语法来指定。

max:

​ 该属性用于指定KeepAlive能缓存的组件实例数量,属性值为一个非负整数。当切换的组件数量大于max属性值时,会自动将最先缓存的组件实例销毁,只保留最近缓存的max个组件实例,避免过渡占用内存资源。

<!-- 属性值为2 只会缓存最近切换的两个组件实例 -->
<KeepAlive max="2"><component :is="currentComponent" />
</KeepAlive>
4、生命周期

​ 当<KeepAlive>内部组件初始挂载时,挂载的组件会进入活跃状态,如果发生组件切换,组件实例会从DOM上移除,但组件会被<KeepAlive>缓存,组件状态变为不活跃状态,当组件重新被激活,挂载到DOM中时,组件状态会再次变为活跃状态。

​ 针对被缓存组件的这两种状态变化,Vue提供了对应的生命周期钩子函数供开发者调用:

使用<script setup>的组件:
<script setup>
import { onActivated, onDeactivated } from 'vue'onActivated(() => {// 调用时机为首次挂载// 以及每次从缓存中被激活时调用
})onDeactivated(() => {// 在及组件卸载时// 以每次进入缓存时调用
})
</script>
未使用<script setup>的组件:
<script>
export default {name: 'KeepaliveSon2',activated() {// 调用时机为首次挂载// 以及每次从缓存中被激活时调用console.log('子组件2被挂载/激活了');},deactivated() {// 在及组件卸载时// 以每次进入缓存时调用console.log('子组件2被缓存/卸载了');},
};
</script>

<KeepAlive>内部发生组件切换时,会先触发被缓存组件的Deactivated钩子函数,再触发要激活组件的Activated钩子函数。但如果项目使用了服务端渲染,则这两个钩子函数在服务器端渲染期间不会被触发。

​ 如果<KeepAlive>缓存的组件内部还嵌套有其他后代组件,则后代组件也可以使用这两个生命周期钩子函数。在被激活时,后代组件的Activated钩子函数先触发,再触发根组件的Activated钩子函数;在被卸载时也是一样,后代组件的Deactivated钩子函数先触发,再触发根组件的Deactivated钩子函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/18716.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解模型驱动测试:优点、挑战与应用场景

本文翻译自&#xff1a;Understanding Model-Based Testing: Benefits, Challenges, and Use Cases 原文作者&#xff1a;Qt Group质量保证市场营销主管Sebastian Polzin 审校&#xff1a;Jinjing Li 对于那些寻求系统化和条理化测试方法的测试工程师而言&#xff0c;模型驱动…

ROS2 Topics和Services

本文主要介绍ROS的Topics概念&#xff0c;如何创建Publisher和Subscriber&#xff0c;通过Topic在ROS程序间通信&#xff1b;介绍ROS的Services概念&#xff0c;如何创建Client和Server并建立通信。 更多内容&#xff0c;访问专栏目录获取实时更新。 ROS Topics Topics可以被视…

做电商,错过了2020年的抖音!那2024一定要选择视频号小店!

哈喽~我是电商月月 电商老板们集合了&#xff0c;问大家一个问题: 如果能让你回到三四年前&#xff0c;抖音才步入大众视野&#xff0c;这时候让你去做抖音小店&#xff0c;你愿意吗&#xff1f; 我敢相信&#xff01;很多&#xff0c;错过当年抖音红利的商家&#xff0c;一…

Docker HTTPS api V2 Manifest V 2, Schema 2 下的免装docker下载镜像的方法

目录 前言 下载镜像代码 使用方法 原代码中无法适配 Schema 2 的原因浅析 如何解决 相对原代码改动的东西 前言 本文提供代码主要是基于 https://github.com/NotGlop/docker-drag 提供的代码修改的。链接中提供的代码应该是是基于HTTPS api V2 Manifest V 2, Schema 1实…

面试必备:应对 “为什么离职” 的万能回答

使用PC端的朋友&#xff0c;请将页面缩小到最小比例&#xff0c;阅读最佳&#xff01; 面试官问到你为什么从上一家公司离职时&#xff0c;你会怎么回答&#xff1f;这个问题我觉得很有意思&#xff0c;也很有必要去探讨一下。 很多专业人士都会建议你&#xff0c;最好不要直接…

C++---迭代器介绍

迭代器的介绍 使用迭代器需要引用头文件,但一般的容器都引用了这个头文件。 这五种迭代器的声明如下: struct output_iterator_tag { };//输出迭代器 struct input_iterator_tag{ };//输入迭代器 struct forward_iterator_tag : public input_iterator_tag {};//向前迭代器 …

基于序列深度学习模型的向量海岸线形状分类方法 2024.05

本文中提出了一个数据驱动的方法来分类的形状矢量海岸线&#xff0c;该方法利用基于序列的深度学习算法对海岸线矢量分段进行建模和分类。具体而言&#xff0c;首先将复杂的海岸线划分为一系列弯曲&#xff0c;并进一步提出了一组不同的特征来描述每个弯曲的形态特征。然后&…

强化学习——学习笔记2

在上一篇文章中对强化学习进行了基本的概述&#xff0c;在此篇文章中将继续深入强化学习的相关知识。 一、什么是DP、MC、TD&#xff1f; 动态规划法&#xff08;DP&#xff09;&#xff1a;动态规划法离不开一个关键词&#xff0c;拆分 &#xff0c;就是把求解的问题分解成若…

gif帧数修改怎么操作?一键掌握GIF帧数修改技巧!

gif帧数修改怎么操作&#xff1f;在数字化信息爆炸的时代&#xff0c;GIF动图因其生动有趣的特性而备受广大网友喜爱。然而&#xff0c;很多时候我们可能会遇到GIF动图帧数过多或过少&#xff0c;导致动画效果不尽如人意的情况。那么&#xff0c;如何对GIF动图的帧数进行修改呢…

探索微软Edge开发者工具:优化前端开发的艺术与科学

探索微软Edge开发者工具&#xff1a;优化前端开发的艺术与科学 引言&#xff1a;Edge开发者工具概览一、基础操作&#xff1a;步入DevTools的大门1.1 启动与界面布局1.2 快速导航与定制 二、元素审查与样式调整2.1 精准元素选取2.2 实时CSS编辑2.3 自动完成与内联文档 三、Java…

YOLOv10最详细全面讲解1- 目标检测-准备自己的数据集(YOLOv5,YOLOv8均适用)

YOLOv10没想到出来的如此之快&#xff0c;作为一名YOLO的爱好者&#xff0c;以YOLOv5和YOLOv8的经验&#xff0c;打算出一套从数据集装备->环境配置->训练->验证->目标追踪全系列教程。请大家多多点赞和收藏&#xff01;&#xff01;&#xff01;YOLOv5和YOLOv8亲测…

Redis中的数据结构与内部编码

本篇文章主要是对 Redis 常见的数据结构进行讲解&#xff0c;同时还对其所对应的不同的内部编码进行讲解。希望本篇文章会对你有所帮助。 文章目录 一、五大数据结构 二、数据结构对应的编码方式 String hash list set zset &#x1f64b;‍♂️ 作者&#xff1a;Ggggggtm &…

js 面试题学习笔记一

1、什么是防抖和节流&#xff1f;有什么区别&#xff1f;如何实现&#xff1f; 防抖&#xff1a;触发高频事件后N秒内函数只会执行一次&#xff0c;如果N秒高频事件再次被触发&#xff0c;则重新计算时间。&#xff08;a时间触发&#xff0c;5秒内执行一次&#xff0c;但是第4…

10G UDP协议栈 (9)UDP模块

目录 一、UDP协议简单介绍 二、UDP功能实现 三、仿真 一、UDP协议简单介绍 UDP协议和TCP协议同位于传输层&#xff0c;介于网络层&#xff08;IP&#xff09;和应用层之间&#xff1a;UDP数据部分为应用层报文&#xff0c;而UDP报文在IP中承载。 UDP 报文格式相对于简单&am…

电脑出现:excel词典(xllex.dll)文件丢失或损坏的错误提示怎么办?有效的将丢失的xllex.dll修复

当遇到 Excel 提示“词典 (xllex.dll) 文件丢失或损坏”的问题时&#xff0c;通常意味着该动态链接库文件&#xff08;Dynamic Link Library&#xff0c;DLL&#xff09;&#xff0c;它与拼写检查功能相关联的&#xff0c;无法被正确找到或者合适地使用。那么有什么办法可以解决…

LLVM技术在GaussDB等数据库中的应用

目录 LLVM和数据库 LLVM适用场景 LLVM对所有类型的SQL都会有收益吗&#xff1f; LLVM在OLTP中就一定没有收益吗&#xff1f; GaussDB中的LLVM 1. LLVM在华为应用于数据库的时间线 2. GaussDB LLVM实现简析 3. GaussDB LLVM支持加速的场景 支持LLVM的表达式&#xff1a…

vue项目出现多次ElMessage

问题&#xff1a; 解决方法&#xff1a; let message null if (message null) { message ElMessage.error(“登录过期,请重新登录”); } 最终效果&#xff1a;只出现一个弹框

Orange AIpro Color triangle帧率测试

OpenGL概述 OpenGL ES是KHRNOS Group推出的嵌入式加速3D图像标准&#xff0c;它是嵌入式平台上的专业图形程序接口&#xff0c;它是OpenGL的一个子集&#xff0c;旨在提供高效、轻量级的图形渲染功能。现推出的最新版本是OpenGL ES 3.2。OpenGL和OpenCV OpenCL不同&#xff0c;…

实操专区-第15周-课堂练习专区-漏斗图与金字塔图

实操专区-第15周-课堂练习专区-漏斗图 下载安装ECharts&#xff0c;完成如下样式图形。 代码和截图上传 基本要求&#xff1a;下图3选1&#xff0c;完成代码和截图 完成 3.1.3.16 漏斗图中的任务点 基本要求&#xff1a;2个选一个完成&#xff0c;多做1个加2分。 请用班级学号姓…

【Java EE】网络原理——HTTP请求

目录 1.认识URL 2.认识“方法&#xff08;method&#xff09;” 2.1GET方法 2.1.1使用Fiddler观察GET请求 2.1.2 GET请求的特点 2.2 POST方法 2.2.1 使用FIddler观察POST方法 2.2.2 POST请求的特点 3.认识请求“报头”&#xff08;header&#xff09; 3.1 Host 3.2 C…