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 = son2
  show.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钩子函数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Sora,数据驱动的物理引擎

文生视频技术 Text-to-Video 近日&#xff0c;Open AI发布文生视频模型Sora&#xff0c;能够生成一分钟高保真视频。人们惊呼&#xff1a;“真实世界将不再存在。” Open AI自称Sora是“世界模拟器”&#xff0c;让“一句话生成视频”的AI技术向上突破了一大截&#xff0c;引…

AI早班车5.22

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是「奇点」&#xff0c;江湖人称 singularity。刚工作几年&#xff0c;想和大家一同进步&#x1f91d;&#x1f91d; 一位上进心十足的【Java ToB端大厂…

Linux:进程控制(二.详细讲解进程程序替换)

上次讲了&#xff1a;Linux&#xff1a;进程地址空间、进程控制&#xff08;一.进程创建、进程终止、进程等待&#xff09; 文章目录 1.进程程序替换1.1概念1.2原理1.3使用一个exec 系列函数execl&#xff08;&#xff09;函数结论与细节 2.多进程时的程序替换3.其他几个exec系…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-24.3,4 SPI驱动实验-I.MX6U SPI 寄存器

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

Vue2基本创建项目

简单版项目初始化 新建一个vue2 官网文档&#xff1a;介绍 — Vue.js 先确保下载了vue的脚手架 npm install -g vue-cli npm install -g vue/cli --force vue -V 创建项目 vue create 自己起个名字 选择自己选择特性 选择&#xff1a; Babel&#xff1a;他可以将我们写…

基于模糊PID控制器的汽车电磁悬架控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于模糊PID控制器的汽车电磁悬架控制系统simulink建模与仿真。 2.系统仿真结果 上面的仿真结果是无控制器和LQG的对比&#xff0c;以及有控制器和LQG的对比仿真。 3.核心程…

UE5 UE4 快速定位节点位置

在材质面板中&#xff0c;找到之前写的一个节点&#xff0c;想要修改&#xff0c;但是当时写的比较多&#xff0c;想要快速定位到节点位置. 在面板下方的 Find Results面板中&#xff0c;输入所需节点&#xff0c;找结果后双击&#xff0c;就定位到该节点处。 同理&#xff0c;…

从0开始学会做标书:新手学习做标书制作必修(95节课)

入门框架 电子标书 商务标书 文档排版 技术标书 实操演示 你是否也有同样的问题 1、做标书公司没人教、没人带? 2、如何看懂招标文件? 3、小白零基础能不能学习做标书? 4、商务标、技术标如何得高分? 5、做标书需要什么软件? 6、如何制作电子标书? 7、如何避…

AI菜鸟向前飞 — LangChain系列之十五 - Agent系列:从现象看机制(中篇)一个Agent的“旅行”

Agent基本架构 先谈谈Agent基本架构概念&#xff0c;如果看得云里雾里&#xff0c;等看完本篇之后&#xff0c;再回头看就会豁然开朗的&#xff0c;而我尽量写得更易懂&#xff1a; &#xff09; 这里面会穿插着上一篇的内容&#xff0c;请大家记得往回翻翻&#xff0c;传送门&…

软件性能测试有哪些测试类型和方法?

软件性能测试是一种通过模拟真实用户使用情况&#xff0c;评估软件系统在各种压力和负载下的表现的测试方法。在今天这个讲究效率的时代&#xff0c;软件性能测试是不可或缺的一环。它能帮助开发人员和企业发现潜在的性能问题&#xff0c;提前优化改进&#xff0c;保证软件系统…

一些关于深度聚类以及部分对比学习的论文阅读笔记

目录 资料SwAV问题方法方法的创新点为什么有效有什么可以借鉴的地方聚类Multi-crop 代码 PCL代码 Feature Alignment and Uniformity for Test Time Adaptation代码 SimSiam 资料 深度聚类算法研究综述(很赞&#xff0c;从聚类方法和深度学习方法两个方面进行了总结&#xff0…

OSINT 与心理学:通过开源情报进行剖析和行为分析

在不断发展的心理学领域&#xff0c;人们越来越认识到通过应用开源情报 (OSINT) 方法取得进步的潜力。OSINT 主要以其在安全和情报领域的应用而闻名&#xff0c;并且越来越多地展示其在心理分析和行为分析方面的潜力。本文探讨了 OSINT 和心理学的迷人交叉点&#xff0c;研究如…

JUC笔记

1、什么是 JUC JUC就是 java.util 下的工具包、包、分类等。 普通的线程代码&#xff1a; ThreadRunnable 没有返回值、效率相比入 Callable 相对较低&#xff01;Callable 有返回值&#xff01; 2、线程和进程 线程、进程&#xff0c;如果不能使用一句话说出来的技术&#x…

Firefox浏览器网页上的按钮点击无效解决办法

我在github下点下载经常不好使&#xff0c;查了原因&#xff0c;原来是浏览器的问题。在Firefox浏览器的设置里面&#xff0c;去掉一些cookies的禁用即可。之后&#xff0c;就可以点击按钮成功响应了。

让大模型更聪明——复杂而艰巨的任务

一、引言 在人工智能领域&#xff0c;大模型因其强大的数据处理能力和复杂的结构&#xff0c;成为了推动技术进步的重要力量。然而&#xff0c;要让大模型真正展现出“聪明”的特质&#xff0c;即具备高度的人类智能水平&#xff0c;仍是一项极具挑战性的任务。本文将从数据质…

Clickhouse Bitmap 类型操作总结—— Clickhouse 基础篇(四)

文章目录 创建 Bitmap 对象Bitmap 转换为整数数组计算总数&#xff08;去重&#xff09;值指定start, end 索引生成子 Bitmap指定 start 索引和数量限制生成子 Bitmap指定偏移量生成子 Bitmap是否包含指定元素两个 Bitmap 是否存在相同元素一个是否为另一个 Bitmap 的子集求最小…

[机缘参悟-191] - 《道家-水木然人间清醒1》读书笔记 -14- 关系界限 - 经济和人格上的独立,走向成熟的必经之路,才能更好的谈其他情感(IT)

目录 前言&#xff1a; 1、“友善的孤独者” 2、“外向的孤独者” 3、道不同不相为谋 4、警惕依赖 5、完整独立的个体 6、不必纠正他人的错误&#xff0c;除非他影响了你 7、不再期待别人能理解自己&#xff0c;只有高维向下兼容你的人才能理解你 8、只有高维和同频的…

简单得阴影引导实现

效果如下: 实现方式&#xff1a; 1、引入三方库&#xff1a; implementation io.github.razerdp:BasePopup:3.2.0 2、代码实现 class NewUserGuide3Popup : BasePopupWindow {constructor(activity: Activity) : super(activity)constructor(context: Context) : super(con…

【ArcGIS For JS】前端geojson渲染行政区划图层并加标签

原理 通过DataV工具 生成行政区的geojson&#xff08;得到各区的面元素数据&#xff09;, 随后使用手动绘制featureLayer与Label&#xff0c;并加载到地图。 //vue3加载geojson数据public/geojson/pt.json,在MapView渲染上加载geojson数据 type是"MultiPolygon"fetc…

K-means 聚类模型详解

K-means 聚类模型详解 K-means 是一种常用的无监督学习算法&#xff0c;用于将数据集分成 K 个簇。其目标是最小化各个簇内数据点到簇中心的距离平方和。K-means 广泛应用于图像压缩、市场细分、模式识别等领域。 算法步骤 初始化: 随机选择 K 个初始簇中心&#xff08;质心…