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.pswp.cn/bicheng/18716.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/18716.shtml
英文地址,請注明出處:http://en.pswp.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;一…

計算機三級 網絡技術

一、邊界網關協議BGP BGP是邊界網關協議&#xff0c;是外部而不是內部網關協議&#xff08;是不同自治系統的路由器之間使用的協議&#xff09;一個BGP發言人使用TCP&#xff08;不是UDP&#xff09;與其自治系統的BGP發言人交換路由信息。BGP協議交換路由由信息的節點數是以自…

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;就是把求解的問題分解成若…

【JavaScript腳本宇宙】點燃你的Web開發:數據綁定和MV*框架

逐一剖析&#xff1a;JavaScript框架和庫的概述、特點與應用 前言 在當今技術日新月異的時代&#xff0c;JavaScript庫和框架已成為前端開發的重要工具。這篇文章將詳細介紹六種不同的JavaScript庫和框架&#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親測…

dubbo復習:(13)把服務劃分為不同的group 和version,只有服務端和客戶端group和version匹配才能通信

一、接口定義 package cn.edu.tju.service;public interface DevelopService {String invoke(String param); }二、兩個版本的實現&#xff1a; package cn.edu.tju.service;import org.apache.dubbo.config.annotation.DubboService;DubboService(group "group1"…

bert模型數據集加載方式

數據集構造 無論是機器學習還是深度學習對于數據集的構造都是十分重要。 現記錄一下PyTorch 的 torch.utils.data.Dataset 類的子類。Dataset 類是PyTorch框架中用于處理數據的基本組件&#xff0c;它允許用戶定義自己的數據集類&#xff0c;以滿足特定任務的需求。 Dataset…

重學英語:輸出的重要性

精通一門外語的四要素&#xff1a;聽&#xff0c;說&#xff0c;讀&#xff0c;寫 輸入&#xff1a;聽&#xff0c;讀 輸出&#xff1a;寫&#xff0c;說 因為輸入是我們可以單獨完成&#xff0c;不需要有人互動&#xff0c;所以我們做得最多 輸出練習做得很少&#xff0c;…

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;無法被正確找到或者合適地使用。那么有什么辦法可以解決…