用vue3開(kāi)發(fā)前端項(xiàng)目的話(huà)淳地,組件通信則是必修課怖糊,方式一般有 以下這幾種
- Props(自定義屬性)
- 自定義事件
- v-model(算是Props和自定義事件的結(jié)合,只不過(guò)屬性和事件名稱(chēng)是默認(rèn)設(shè)置的)
- Provide & Inject
- attrs
- ref颇象、parent
- 全局狀態(tài)管理(如 Pinia 或Vuex)
- 事件總線(xiàn)&pubsub(發(fā)布訂閱)
常用的大概就以上這么多伍伤,下面將針對(duì)這幾種方法來(lái)展開(kāi)詳細(xì)的重點(diǎn)說(shuō)明和實(shí)際應(yīng)用。
props
父級(jí)組件用法與vue2相同
<template>
<div class="parent">
<div class="title">
<h1>父組件</h1>
</div>
<child :count="count"></child>
</div>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
</script>
子組件接收父級(jí)自定義屬性則與vue2不同遣钳,vue2中是以props 配置項(xiàng)來(lái)接收扰魂,vue3 中則需要 用到宏函數(shù) defineProps類(lèi)接收
<template>
<div class="child">
<h1>子組件</h1>
<h1>父級(jí)props:{{ count }}</h1>
</div>
</template>
<script setup>
defineProps(['count'])
</script>
如果想給count 設(shè)置類(lèi)型和默認(rèn)值,和vue2設(shè)置方法差不多一樣
defineProps({
count: {
type: Number,
default: 1
}
})
自定義事件
父級(jí)組件用法與vue2相同
父組件
<template>
<div class="parent">
<div class="title">
<h1>父組件: {{ count }}</h1>
<h1 v-show="childCount">子組件給的值:{{ childCount }}</h1>
</div>
<child :count="count" @send-add="addCount"></child>
</div>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
let childCount = ref(0)
function addCount(value) {
childCount.value = value
}
</script>
子組件的化就不能用vue2中的$emit了,需要換成宏函數(shù) defineEmits,參數(shù)為數(shù)組劝评,數(shù)組的元素為父級(jí)的 自定義事件名稱(chēng) sendAdd姐直,defineEmits返回的值一個(gè)對(duì)象,該對(duì)象包含了組件可以觸發(fā)的所有自定義事件
<template>
<div class="child">
<h1>子組件</h1>
<h1>父組件給的值:{{ count }}</h1>
<button @click="emit('sendAdd',2)">CountAdd</button>
</div>
</template>
<script setup>
defineProps({
count: {
type: Number,
default: 1
}
})
let emit = defineEmits(['sendAdd'])
</script>
v-model
- 在vue2中 v-model其實(shí)是 prop為 value和 自定義事件是 input 的語(yǔ)法糖
- 在vue3中同樣蒋畜,只不過(guò) value 變?yōu)榱?modelValue声畏,input改成了update:modelValue 而且子組件中接收 需要用到宏函數(shù)defineModel
還有一點(diǎn)不同的是就是 vue2中只能綁定一個(gè)v-model,但vue3中卻可以綁定多個(gè)
父組件 寫(xiě)法還是v-model
vue版本 3.4之前的寫(xiě)法
用defineProps接受modelValue姻成,defineEmits返回 父組件
<template>
<div class="parent">
<div class="title">
<h1>父組件 count: {{ count }}</h1>
</div>
<child v-model="count"></child>
</div>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
</script>
子組件
<template>
<div class="child">
<h1>子組件</h1>
<button @click="emit('update:modelValue',20)">CountAdd</button>
</div>
</template>
<script setup>
defineProps(['modelValue'])
let emit = defineEmits(['modelValue'])
</script>
從vue 版本3.4后 加入了宏函數(shù) defineModel插龄,支持以下兩種寫(xiě)法
第一種單個(gè)v-model
defineModel會(huì)默認(rèn)接收
<template>
<div class="parent">
<div class="title">
<h1>父組件</h1>
</div>
<child v-model="count"></child>
</div>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
</script>
子組件中 defineModel接收父級(jí)v-model,默認(rèn)的prop為 "modelValue佣渴, 返回的是一個(gè)ref辫狼,通過(guò)修改這個(gè)ref的Value,自動(dòng)觸發(fā) update:modelValue 事件,這樣就不用再使用defineEmits來(lái)觸發(fā)了了辛润,真的很方便
<pre>
xml
代碼解讀
復(fù)制代碼
<template>
<div class="child">
<h1>子組件</h1>
<h1>父組件給的值:{{ modelValue }}</h1>
<button @click="sendAdd">CountAdd</button>
</div>
</template>
<script setup>
let count = defineModel()
function sendAdd(){
count.value = 20
}
</script>
還支持多個(gè) v-model
父
<!--父-->
<template>
<div class="parent">
<div class="title">
<h1>父組件 name: {{ name }}</h1>
<h1>父組件 count: {{ count }}</h1>
</div>
<child v-model:name="name" v-model:count="count"></child>
</div>
</template>
<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
let name = ref('父組件')
</script>
子
<!--子-->
<template>
<div class="child">
<h1>子組件</h1>
<button @click="sendAdd">CountAdd</button>
<button @click="changeName">changeName</button>
</div>
</template>
<script setup>
let count = defineModel('count')
let name = defineModel('name')
function sendAdd(){
count.value = 20
}
function changeName(){
name.value = '子組件'
}
</script>
另外 defineModel 還是支持設(shè)置默認(rèn)值和類(lèi)型
defineModel('count', { required: 10,type: Number,req })
attrs
父給孫傳參數(shù)
下方的 v-bind="{ money: money, spend: spend }
等價(jià)于 :money="money" :spend="spend"
父組件
<template>
<div class="parent">
<div class="title">
<h1>父組件</h1>
<h1>我的錢(qián): {{ money }} 百萬(wàn)</h1>
</div>
<!-- <child :count="count" :money="money"></child> -->
<child v-bind="{ money: money, spend: spend }"></child>
</div>
</template>
<script setup>
import { ref } from "vue";
import child from "./child.vue";
let money = ref(100);
let spend = (num) => {
money.value -= num
}
</script>
子組件 相當(dāng)于中間過(guò)度膨处,把props 里沒(méi)有接收參數(shù),以$attrs傳給孫子組件
<template>
<div class="child">
<h1>子組件</h1>
<grandson v-bind="$attrs"></grandson>
</div>
</template>
<script setup>
import grandson from './grandson.vue'
</script>
孫組件
<template>
<div class="grandson">
<h4>孫組件</h4>
<h4>得到的錢(qián): {{ money }} 百萬(wàn)</h4>
<button @click="spend(1)">消費(fèi)</button>
</div>
</template>
<script setup>
defineProps(['money','spend'])
</script>
Provide & Inject
provide 和 inject 通常會(huì)在不同的組件中運(yùn)行,最頂層組件 provide提供數(shù)據(jù)(依賴(lài)注入)砂竖,所有子組件用 inject(注入) 來(lái)接收真椿, 降低耦合度、提高可重用性乎澄、易于管理突硝、功能增強(qiáng)
父組件
<template>
<div class="parent">
<div class="title">
<h1>父組件</h1>
<h1>我的錢(qián): {{ money }} 百萬(wàn)</h1>
</div>
<child></child>
</div>
</template>
<script setup>
import { ref, provide } from 'vue';
import child from './child.vue'
let money = ref(100)
provide('money', money)
provide('spend', (num) => {
money.value -= num
})
</script>
子組件
<template>
<div class="child">
<h1>子組件</h1>
<grandson></grandson>
</div>
</template>
<script setup>
import grandson from './grandson.vue'
</script>
孫組件
<template>
<div class="grandson">
<h4>孫組件</h4>
<h4>得到的錢(qián): {{ money }} 百萬(wàn)</h4>
<button @click="spend(1)">消費(fèi)</button>
</div>
</template>
<script setup>
import { inject } from 'vue'
let money = inject('money')
let spend = inject('spend')
</script>
inject 第二個(gè)參數(shù)還能設(shè)置默認(rèn)值,當(dāng)provide沒(méi)有以來(lái)注入'money'這個(gè)關(guān)鍵字時(shí),默認(rèn)值才會(huì)生效
inject('money',200)
ref置济、parent
ref寫(xiě)法不變解恰,但獲取組件實(shí)例的方法有所改動(dòng),組件上ref綁定一個(gè)關(guān)鍵字(cc)浙于,js中 需要聲明一個(gè)ref綁定的關(guān)鍵字的同名字 let cc = ref()护盈,通過(guò)cc就能訪問(wèn)到組件的實(shí)例
父組件
<template>
<div class="parent">
<div class="title">
<h1>父組件</h1>
<h1>父親的錢(qián):{{ count }}</h1>
<button @click="reduce">減少孩子的錢(qián)</button>
</div>
<child ref="cc"></child>
</div>
</template>
<script setup>
import { ref } from "vue";
import child from "./child.vue";
let cc = ref();
let count = ref(1000);
let reduce = () => {
console.log(cc.value);
cc.value.money -= 1;
count.value += 1;
};
<!--defineExpose({count})-->
</script>
子組件
<template>
<div class="child">
<h1>子組件</h1>
<h1>name: {{ name }}</h1>
<h1>age: {{ age }}</h1>
<h1>money: {{ money }}</h1>
<button @click="add($parent)">要回自己的錢(qián)</button>
</div>
</template>
<script setup>
import { ref } from "vue";
let name = ref("我是子組件");
let age = ref(18);
let money = ref(500);
let add = (parent) => {
console.log(parent)
money.value += 1;
parent.count -= 1;
};
<!--defineExpose({ money });-->
</script>
點(diǎn)擊按鈕打印實(shí)例會(huì)發(fā)現(xiàn) 實(shí)例中沒(méi)有子組件的數(shù)據(jù)源,那是因?yàn)関ue 3 加入了 defineExpose羞酗。需要子組件 在defineExpose中聲明 被允許訪問(wèn)的數(shù)據(jù)源腐宋,沒(méi)被聲明的將無(wú)法被訪問(wèn)。
子組件中 加入以下代碼 money 才能被訪問(wèn)
defineExpose({money})
同樣父組件中聲明被允許訪問(wèn)的數(shù)據(jù)源頭
defineExpose({count})
pinia
pinane
可以理解為下一代 vuex檀轨,作者也稱(chēng)之為vuex5胸竞,同時(shí)vue已經(jīng)將 pinia 收入 官方賬戶(hù)了
事件總線(xiàn) mitt
安裝 mitt 庫(kù):
npm install mitt
然后,創(chuàng)建一個(gè)事件總線(xiàn)的模塊 eventBus.js:
import mitt from'mitt';
const eventBus = mitt();
export default eventBus;
在需要發(fā)送事件的組件中:
`import eventBus from './eventBus';
// 發(fā)送事件
eventBus.emit('myEvent', { data: '這是事件攜帶的數(shù)據(jù)' });
在需要接收事件的組件中:
`import eventBus from './eventBus';
// 監(jiān)聽(tīng)事件
eventBus.on('myEvent', (payload) => {
console.log('接收到事件:', payload);
});
這樣就實(shí)現(xiàn)了一個(gè)基本的 事件總線(xiàn)参萄,用于組件之間的通信卫枝。