什么是組件?
在 Vue3 中,組件是構(gòu)建應(yīng)用界面的核心概念之一。組件可以看作是可復(fù)用、自包含和可組合的代碼塊,用于封裝 UI 元素和相應(yīng)的行為邏輯。
通俗來說就是,組件(Component)是一種對(duì)數(shù)據(jù)和方法的簡單封裝,每一個(gè)組件有自己單獨(dú)的邏輯,并且可以分別管理。不同的組件組合在一起,就形成了頁面。所以,每一個(gè)Web頁面可以抽象成是不同組件組合而成的,頁面只是這些組件的一個(gè)容器。并且這些組件可以在不影響程序運(yùn)行的情況下,隨時(shí)被替換,利用這種組件化的思想可以將一個(gè)巨大的東西拆分成很多小模塊,也是現(xiàn)代前端框架的核心思想之一。
在 Vue 中,通常一個(gè)應(yīng)用會(huì)以一顆嵌套的組件樹的形式來組織,如圖所示:
上圖中將整體頁面為一個(gè)根組件,然后根組件下有三個(gè)子組件,分別是頁頭組件、內(nèi)容區(qū)組件和側(cè)邊欄組件。在內(nèi)容區(qū)組件下,又細(xì)分出兩個(gè)內(nèi)容組件,而側(cè)邊欄組件則有三個(gè)側(cè)邊欄內(nèi)容組件。所有組件整齊排列,按照樹形結(jié)構(gòu)組合,這就是 Vue 內(nèi)組件的組織結(jié)構(gòu)。
另外 Vue 3 的組件還具有以下特點(diǎn):
-
模塊化:組件以模塊化的方式進(jìn)行定義和使用,每個(gè)組件都有獨(dú)立的作用域,使得代碼更加結(jié)構(gòu)化和可維護(hù)。組件可根據(jù)需要進(jìn)行拆分,形成層次化的組件樹結(jié)構(gòu)。
-
復(fù)用性:組件是可復(fù)用的,可以在應(yīng)用中多次使用。通過將 UI 元素和相關(guān)的邏輯封裝為組件,可以避免代碼重復(fù),并且可以輕松地在不同的上下文中重用組件。
-
可組合性:組件可以通過父子組件之間的嵌套與組合,形成更大規(guī)模的應(yīng)用界面。通過傳遞屬性 prop 和監(jiān)聽事件 emit,組件之間可以進(jìn)行數(shù)據(jù)和通信的交互。
-
響應(yīng)式:組件內(nèi)部的數(shù)據(jù)可以使用 Vue 3 的響應(yīng)式系統(tǒng)進(jìn)行管理,當(dāng)數(shù)據(jù)發(fā)生變化時(shí),組件會(huì)自動(dòng)更新視圖。通過響應(yīng)式系統(tǒng),可以實(shí)現(xiàn)數(shù)據(jù)的雙向綁定和自動(dòng)渲染。
-
生命周期鉤子函數(shù):Vue 3 的組件具有一系列的生命周期鉤子函數(shù),用于在組件不同的生命周期階段執(zhí)行特定的代碼邏輯。生命周期鉤子函數(shù)可以幫助開發(fā)者控制組件的行為和實(shí)現(xiàn)特定的功能。
-
單文件組件:Vue 3 支持使用單文件組件(.vue 文件)的方式來定義和編寫組件。單文件組件將組件的模板、樣式和邏輯都封裝在一個(gè)文件中,提高了代碼的可讀性和維護(hù)性。
組件定義
一個(gè) Vue 組件的結(jié)構(gòu)通常包括三個(gè)部分:模板(Template)、腳本(Script)和樣式(Style)。這些部分一般會(huì)放在一個(gè)單文件組件(.vue 文件)中,也可以分離成三個(gè)獨(dú)立的文件。
下面是一個(gè)典型的 Vue 組件的結(jié)構(gòu)示例:
<template>
<!-- 模板部分 -->
</template>
<script setup>
// 腳本部分
</script>
<style>
/* 樣式部分 */
/* CSS 樣式規(guī)則 */
</style>
其中:
-
模板(Template):模板部分定義了組件的結(jié)構(gòu)和布局,使用 HTML 和 Vue 的模板語法編寫。在模板中可以插入動(dòng)態(tài)數(shù)據(jù)和表達(dá)式,并通過指令(如
v-bind
、v-if
)和事件綁定等方式與組件的數(shù)據(jù)和行為進(jìn)行交互。 -
腳本(Script):腳本部分是組件的邏輯核心,使用 JavaScript 或 TypeScript 編寫。在腳本中,可以定義組件的屬性、計(jì)算屬性、方法、生命周期鉤子函數(shù)等。通過腳本,可以處理組件的數(shù)據(jù)邏輯、事件響應(yīng)等功能。
-
樣式(Style):樣式部分定義了組件的樣式規(guī)則,使用 CSS 或預(yù)處理器(如 Sass、Less)編寫。可以為組件元素添加類名、樣式選擇器等,來對(duì)組件進(jìn)行樣式修飾和美化。
這種將模板、腳本和樣式封裝在一個(gè)單文件組件中的方式,可以提高代碼的可讀性和維護(hù)性,并且使得組件的結(jié)構(gòu)更加清晰和獨(dú)立。通過單文件組件,我們可以更好地組織和管理組件的相關(guān)資源,并方便地重用和維護(hù)組件。
組件注冊(cè)
在 Vue 中,如果要使用自定義組件,第一步需要做的就是將其注冊(cè)到應(yīng)用中。Vue 組件的注冊(cè)可以有兩種方式:全局注冊(cè)和局部注冊(cè)。
-
全局注冊(cè):全局注冊(cè)的組件可以在整個(gè)應(yīng)用的任何地方使用,無需額外的導(dǎo)入或注冊(cè)操作。
全局注冊(cè)組件的方法是在 main.ts 文件中使用
app.component
方法。例如,將自定義組件my-component
進(jìn)行全局注冊(cè):import { createApp } from 'vue'; import App from './App.vue'; import MyComponent from './components/MyComponent.vue'; const app = createApp(App); app.component('my-component', MyComponent); app.mount('#app');
在上述示例中,我們首先使用
createApp
創(chuàng)建應(yīng)用實(shí)例,并將根組件App
作為參數(shù)傳入。然后,使用app.component
方法全局注冊(cè)名為my-component
的組件,并將其與MyComponent
組件關(guān)聯(lián)。最后,通過app.mount
將應(yīng)用掛載到頁面中的 DOM 元素上。在全局注冊(cè)后,我們可以在任何組件的模板中使用
<my-component></my-component>
標(biāo)簽來引入和使用該組件,無需在局部組件中重新注冊(cè)。 -
局部注冊(cè):局部注冊(cè)的組件只能在其所屬的組件內(nèi)部使用,無法在其他組件(包括子組件)中直接使用。
在組合式 API 中組件局部注冊(cè)時(shí)直接引入,然后直接調(diào)用即可。例如:
<script setup> import MyComponent from './MyComponent.vue'; </script> <template> <!-- 使用局部注冊(cè)的子組件 --> <my-component></my-component> </template>
在上述代碼中,
<script setup>
部分引入了MyComponent
組件,并且不需要使用app.component
進(jìn)行額外的局部注冊(cè)。直接在模板中使用<my-component></my-component>
即可使用這個(gè)局部注冊(cè)的組件。
通過全局或局部注冊(cè)組件,我們可以在應(yīng)用中靈活地使用和組織組件,實(shí)現(xiàn)不同層次的封裝和復(fù)用。
組件通信
在 Vue 中,組件之間的通信,大致可以分為以下四種情況:父組件向子組件通信、子組件向父組件通信、父組件向隔代子組件通信和非父子組件之間的通信。如圖:
四種通信模式涵蓋了 Vue 中組件和組件通信的所有情況,解決方法如下:
- 父組件向子組件傳遞數(shù)據(jù):使用組件的 props 實(shí)現(xiàn)。
- 子組件向父組件傳遞數(shù)據(jù):通過自定義事件實(shí)現(xiàn)。
- 父組件向隔代子組件傳遞數(shù)據(jù):使用訂閱發(fā)布實(shí)現(xiàn),使用 provide/inject 在父組件提供數(shù)據(jù),在后代組件中注入數(shù)據(jù)。
- 非父子組件之間傳遞數(shù)據(jù):使用第三方狀態(tài)管理實(shí)現(xiàn),如 Pinia。
根據(jù)不同的場景和需求,選擇適合的通信方式來進(jìn)行組件之間的通信。
使用 Props 通信
Props 關(guān)鍵字代表開發(fā)者在組件上注冊(cè)的一些自定義屬性,然后父組件通過子組件的 Props 屬性將數(shù)據(jù)直接傳遞到子組件內(nèi)部,供子組件調(diào)用處理。
需要注意的是,Props 傳遞的數(shù)據(jù)全部為單向流數(shù)據(jù),即只能從父組件向子組件傳遞,不能子組件向父組件傳遞。所以,在使用 Props 進(jìn)行組件之間通信時(shí),需要首先在子組件內(nèi)定義 Props,然后在父組件內(nèi)調(diào)用,代碼如下:
//子組件 ChildComponent.vue
<template>
<p>{{ message }}</p>
</template>
<script setup lang="ts">
defineProps<{
message?:string
}>()
</script>
在子組件中,使用 defineProps()
方法定義了一個(gè)名為 message
的 prop,類型為字符串。通過在模板中使用雙花括號(hào)語法 {{ message }}
,可以顯示接收到的來自父組件的數(shù)據(jù)。
//父組件 Parent.vue
<script setup lang="ts">
import ChildComponent from './ChildComponent.vue'
const parentMessage=ref<string>("Hello from parent component!")
</script>
<template>
<child-component :message="parentMessage"></child-component>
</template>
在父組件中,定義了一個(gè)名為 parentMessage
的數(shù)據(jù)屬性,并將其綁定到子組件的 prop 上。通過在子組件標(biāo)簽上使用冒號(hào)語法 :message="parentMessage"
,將父組件的 parentMessage
數(shù)據(jù)傳遞給子組件的 message
prop。
使用以上代碼,父組件就能夠通過 props 向子組件傳遞數(shù)據(jù)了。在子組件中,可以直接使用該數(shù)據(jù)進(jìn)行展示或進(jìn)一步處理。這種父組件向子組件傳遞數(shù)據(jù)的方式非常常見,并且在 Vue 組件開發(fā)中被廣泛使用。
另外,如果想給 Props 定義的屬性一個(gè)初始值,可以使用 withDefaults()
方法,代碼如下:
<script setup lang="ts">
withDefaults(defineProps<{message?:string}>(),{
message:'這是默認(rèn)值'
})
</script>
withDefaults()
方法接收的第一個(gè)參數(shù)是 Props 定義,第二個(gè)參數(shù)則是這些 Props 定義的屬性的默認(rèn)值。并且這里在定義 Props 的時(shí)候,采用 TypeScript 的可選屬性定義組件屬性,即再屬性名稱后面添加問號(hào),代表當(dāng)前 Props 屬性為非必傳屬性,子組件使用默認(rèn)值來進(jìn)行渲染。
使用自定義事件通信
在 Vue 3 的組合式 API 中,組件的自定義事件是通過 defineEmits()
方法定義。然后在需要被調(diào)用的地方發(fā)射自定義事件給父組件,同時(shí),父組件通過 v-on
指令將本身的處理函數(shù)與子組件發(fā)射的自定義事件相綁定,從而實(shí)現(xiàn)子組件向父組件通信的過程。
下面是一個(gè)示例代碼:
//子組件 ChildComponent.vue
<template>
<button @click="emit('btnClick', message)">傳遞</button>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const message = ref<string>("子組件消息")
const emit = defineEmits<{
(eventName: 'btnClick', message: string): void
}>()
</script>
上述代碼中,我們?cè)?<script setup>
中,使用 defineEmits
來定義了一個(gè) btnClick
的自定義事件。然后,我們將 sendMessage
方法進(jìn)行了修改,使其調(diào)用 emit
方法并傳遞了 btnClick
事件名稱和 message
變量的值。
這樣,當(dāng)子組件的按鈕被點(diǎn)擊時(shí),就會(huì)觸發(fā) btnClick
事件,并將 message
的值傳遞給父組件。父組件可以監(jiān)聽該事件并進(jìn)行相應(yīng)的處理。
//父組件 Parent.vue
<script>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const message = ref<string>("父組件消息")
const handle = (msg:string):void=>{
message.value=msg
}
</script>
<template>
<p>消息: {{ message }}</p>
<child-component @btn-click="handle"></child-component>
</template>
在父組件中,我們使用 @btn-click
監(jiān)聽子組件觸發(fā)的 btn-click
自定義事件,并將 handle
方法指定為事件處理函數(shù)。
當(dāng)子組件中的按鈕被點(diǎn)擊時(shí),會(huì)觸發(fā) btn-click
事件并將消息作為參數(shù)傳遞給父組件的 handle
方法。在 handle
方法中,我們將接收到的消息更新到 message
變量上,從而實(shí)現(xiàn)父組件接收子組件發(fā)送的消息并進(jìn)行處理的效果。
使用訂閱發(fā)布通信
Props 屬性負(fù)責(zé)父組件向子組件傳遞數(shù)據(jù),子組件可以通過自定義事件向父組件傳遞數(shù)據(jù)。但是如果兩個(gè)組件不是父子組件關(guān)系,而是深度嵌套的組件,并且深層子組件只需要父組件的部分內(nèi)容,這個(gè)時(shí)侯如果使用 Props 屬性逐級(jí)傳下去,將會(huì)顯得非常麻煩而且容易出錯(cuò)。針對(duì)這種情況,Vue 推出了發(fā)布訂閱進(jìn)行通信,即 Provider/Inject 通信。
Provider/Inject 通信,需要有一個(gè) Provider 和一個(gè)或者多個(gè) Inject。在父組件中,Provider 負(fù)責(zé)提供數(shù)據(jù),深層子組件里的 Inject 負(fù)責(zé)讀取數(shù)據(jù)。這種通信方式,不管父子組件中間相隔多久,都是可以實(shí)現(xiàn)的。
例如這里的組件層級(jí)關(guān)系如下:
ProjectInjectComponent
﹂ProjectInjectChild
﹂ProjectInjectGrandson
在 ProjectlnjectComponent 中使用 Provide 先發(fā)布一條數(shù)據(jù), 然后在孫組件 ProjectInjectGrandson 中通過 Inject 訂閱這條數(shù)據(jù)并顯示。代碼如下:文章來源:http://www.zghlxwxcb.cn/news/detail-646953.html
//ProjectInjectComponent.vue(父組件)
<script lang="ts" setup>
import { provide, ref } from 'vue';
import ProjectInjectChild from './ProjectInjectChild.vue';
const message = ref<string>("ProjectInjectComponent 組件消息")
//發(fā)布數(shù)據(jù),key:message
provide('message',message);
</script>
<template>
<ProjectInjectChild></ProjectInjectChild>
</template>
//ProjectInjectChild.vue(子組件)
<script lang="ts" setup>
import ProjectInjectGrandson from './ProjectInjectGrandson.vue';
</script>
<template>
<ProjectInjectGrandson></ProjectInjectGrandson>
</template>
//ProjectInjectGrandson.vue(孫組件)
<script lang="ts" setup>
import { inject, ref } from 'vue';
//訂閱數(shù)據(jù) key:message,defaultValue:"消息"
const message = inject<string>('message',"消息")
</script>
<template>
<h2>{{ message }}</h2>
</template>
在 ProjectInjectComponent 組件中通過 Provide 發(fā)布了一個(gè)名為 message 數(shù)據(jù);在嵌套最底層的 ProvideInjectGrandson 組件中通過 Inject 讀取父組件發(fā)布的 message 數(shù)據(jù),并渲染在頁面上。因?yàn)楦附M件中的 message 是響應(yīng)式數(shù)據(jù),所以父組件中的數(shù)據(jù)會(huì)同步更新到嵌套的子組件中,最終將更新后的數(shù)據(jù)重新渲染到頁面。文章來源地址http://www.zghlxwxcb.cn/news/detail-646953.html
到了這里,關(guān)于深入了解 Vue 3 組件間通信機(jī)制的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!