Vue 組件的通訊方式都有哪些?
- 2021 年 2 月 18 日
- 筆記
說一下 Vue 組件的通訊方式都有哪些?(父子組件,兄弟組件,多級嵌套組件等等)
- 一、父組件向子組件傳值
- 二、子組件向父組件傳值
- 三、兄弟組件傳值
- 四、跨組件
一、父組件向子組件傳值
1.1props
方式:可以是數組或對象,用於接收父組件的數據
<div id="app">
<child-component :msg="message" :count="count"></child-component>
<button @click="count++">
點擊+1
</button>
</div>
<script>
const childComponent = {
props: {
msg: String
count: Number
},
template: `<div><p>{{msg}}</p><p>{{count}}</p><div>`
}
new Vue({
el: '#app',
data: {
message:'父組件的值',
count:0
},
components:{
childComponent
}
})
</script>
1.2 通過$parent
獲取父組件實例的方法或者屬性
這種方式,從嚴格意義上講不是值的傳遞,而是一種 「取」 (不推薦直接通過實例進行值的獲取)
實例屬性
$parent
可以獲得父組件的實例,藉助實例可以調用父實例中的方法,或者父實例上的屬性
<div id="app">
<child-component :msg="message" :count="count"></child-component>
<button @click="count++">
點擊+1
</button>
</div>
<script>
//子組件
const childComponent = {
data:() => ({
msg:'',
count:null
}),
methods: {
handleClick(){
//調用父級實例的方法
this.$parent.parentMethods();
}
}
mounted () {
// 獲取父級實例中的屬性
this.msg = this.$parent.message;
this.count = this.$parent.count;
}
template: `<div><p @click="handleClick">{{msg}}</p><p>{{count}}</p><div>`
}
// 父級
new Vue({
el: '#app',
data: {
message: '父組件的值',
count: 0
},
methods: {
parentMethod () {
console.log('我是父級的方法');
}
}
components: {
childComponent
}
})
</script>
1.3 使用修飾符.sync
修飾符.sync
是2.3.0+
新增,它對props
起到了一種修飾的作用,使用.sync
進行修飾的props
,意味著子組件有修改它的意圖,這種情況下它只起到一個標註性作用,有它沒它都不會影響邏輯
使用.sync
修改上邊的程式碼:
// 父組件 List.vue
<template>
<!-- 這裡不寫 .sync 也不會影響結果 -->
<List-item :title.sync="title" @update:title="updataTitle"></List-item>
</template>
<script>
import ListItem from "./ListItem";
export default {
data() {
return {
title: "我是title",
}
},
components: {
ListItem
},
methods: {
updataTitle(res) {
this.title = res;
}
}
}
</script>
// 子組件 ListItem.vue
<template>
<div>
<button @click="handleClick">Click me</button>
<div>{{title}}</div>
</div>
</template>
<script>
export default {
props: {
title: String,
},
methods: {
handleClick() {
// 子組件向父組件傳值
this.$emit('update:title', '我要父組件更新 title');
}
}
}
</script>
使用.sync
向子組件傳遞多個props
:
當我們用一個對象同時設置多個prop
的時候,也可以將這個.sync
修飾符和v-bind
配合使用:
<text-document v-bind.sync="doc"></text-document>
這樣會把doc
對象中的每一個屬性(如:title)都作為一個獨立的prop
傳進去,然後各自添加用於更新的v-on
監聽器。
二、子組件向父組件傳值
2.1 通過事件傳值$emit
使用:
子組件使用
$emit
發送一個自定義事件父組件使用指令
v-on
監聽子組件發送的事件
<div id="app">
<child-component @child-event="childEvent"></child-component>
</div>
<script>
//子組件
const childComponent={
data:()=>({
msg:'點擊發送值到父組件',
count:null
}),
methods:{
handleClick(data){
this.$emit('child-event','我是子組件傳過來的值');
}
}
template:`<div><p @click="handleClick">{{msg}}</p><p>{{count}}</p><div>`
}
// 父級
new Vue({
el:'#app',
data:{
message:'父組件的值',
count:0
},
methods:{
childEvent(data){
console.log("子組件傳過來的值",data);
}
}
components:{
childComponent
}
})
</script>
2.2 通過$children
獲取子組件實例
同$parent
2.3 通過ref
註冊子組件引用
雖然存在prop
和事件,但是有時仍可能需要在 JavaScript 里直接訪問一個子組件。為了實現這樣的需求,可以使用ref
特性為某個子組件設置一個 ID 引用,就是一個身份標識
<div id="app">
<child-component ref="childComponent"></child-component>
<button @click="getRefs">
獲取子組件實例
</button>
</div>
<script>
//子組件
const childComponent={
data:()=>({
msg:'點擊發送值到父組件',
count:null
}),
template:`<div><p @click="handleClick">{{msg}}</p><p>{{count}}</p><div>`
}
// 父級
new Vue({
el:'#app',
data:{
message:'父組件的值',
count:0
},
methods:{
getRefs(){
console.log("子組件傳過來的值",this.$refs.childComponent.msg);
}
}
components:{
childComponent
}
})
</script>
三、兄弟組件傳值
3.1Bus
中央事件匯流排
非父子組件傳值,可以使用一個空的Vue
實例作為中央事件匯流排,結合實例方法$on
,$emit
使用
注意:
註冊的
Bus
要在組件銷毀時卸載,否則會多次掛載,造成觸發一次但多個響應的情況。
beforeDestroy(){
this.$Bus.$off('方法名',value);
}
Bus
定義方式:
-
1,將
Bus
抽離出來,組件有需要時引入// bus.js import Vue from 'vue'; const Bus = new Vue(); export default Bus;
-
2,將
Bus
掛載到 Vue 根實例的原型上import Vue from 'vue'; Vue.prototype.$bus = new Vue();
-
3,將
Bus
注入到 Vue 根對象上import Vue form 'vue'; const Bus = new Vue(); new Vue({ el:'#app', data:{ Bus } })
使用例子:
<div id="app"> <child-component ></child-component> <child-component-two ></child-component-two> </div> <script> Vue.prototype.$bus = new Vue(); //子組件1 const childComponent={ data:()=>({ msg:'我是子組件一', sendMsg:'我是子組件一發送的值' }), methods:{ handleClick(){ this.$Bus.$emit('sendMsg',this.sendMsg); } } template:`<div><p @click="handleClick">{{msg}}</p></p><div>` } //子組件2 const childComponentTwo={ data:()=>({ msg:'我是子組件二', brotherMsg:'' }), mounted(){ this.$Bus.$on('sendMsg',data=>{ this.brotherMsg = data; }) }, beforeDestroy(){ this.$Bus.$off('sendMsg'); } template:`<div><p @click="handleClick">{{msg}}</p><p>{{brotherMsg}}</p><div>` } // 父級 new Vue({ el:'#app', data:{}, components:{ childComponent, childComponentTwo } }) </script>
四、跨組件
4.1$attrs
和$listeners
如果父組件 A 下面有子組件 B,組件 B 下面有組件 C,這時如果組件 A 直接想傳遞數據給 組件 C,那就行不通了!所以,這時可以使用$attrs
和$listeners
Vue 2.4 提供了$attrs
和$listeners
來實現能夠直接讓 組件 A 傳遞消息給 組件 C
// 組件A
Vue.component('A', {
template: `
<div>
<p>this is parent component!</p>
<B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
</div>
`,
data() {
return {
message: 'hello',
messagec: 'hello c' //傳遞給c組件的數據
}
},
methods: {
// 執行B子組件觸發的事件
getChildData(val) {
console.log(`這是來自B組件的數據:${val}`);
},
// 執行C子組件觸發的事件
getCData(val) {
console.log(`這是來自C組件的數據:${val}`);
}
}
});
// 組件B
Vue.component('B', {
template: `
<div>
<input type="text" v-model="mymessage" @input="passData(mymessage)">
<!-- C組件中能直接觸發 getCData 的原因在於:B組件調用 C組件時,使用 v-on 綁定了 $listeners 屬性 -->
<!-- 通過v-bind 綁定 $attrs 屬性,C組件可以直接獲取到 A組件中傳遞下來的 props(除了 B組件中 props聲明的) -->
<C v-bind="$attrs" v-on="$listeners"></C>
</div>
`,
/**
* 得到父組件傳遞過來的數據
* 這裡的定義最好是寫成數據校驗的形式,免得得到的數據是我們意料之外的
*
* props: {
* message: {
* type: String,
* default: ''
* }
* }
*
*/
props: ['message'],
data () {
return {
mymessage: this.message
}
},
methods: {
passData(val){
//觸發父組件中的事件
this.$emit('getChildData', val)
}
}
});
// 組件C
Vue.component('C', {
template: `
<div>
<input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)">
</div>
`,
methods: {
passCData(val) {
// 觸發父組件A中的事件
this.$emit('getCData',val)
}
}
});
var app = new Vue({
el:'#app',
template: `
<div>
<A />
</div>
`
});
4.2provide
和inject
熟悉 React 開發的同學對 Context API 肯定不會陌生吧!在 Vue 中也提供了類似的 API 用於組件之間的通訊。在父組件中通過provide
來提供屬性,然後在子組件中通過inject
來注入變數。不論子組件有多深,只要調用了inject
,那麼就可以注入在provide
中提供的數據,而不是局限於只能從當前父組件的prop
屬性來獲取數據,只要在父組件的生命周期內,子組件都可以調用。這和 React 中的 Context API 有沒有很相似!
// 定義 parent 組件
Vue.component('parent', {
template: `
<div>
<p>this is parent component!</p>
<child></child>
</div>
`,
provide: {
for:'test'
},
data() {
return {
message: 'hello'
}
}
});
// 定義 child 組件
Vue.component('child', {
template: `
<div>
<input type="tet" v-model="mymessage">
</div>
`,
inject: ['for'], // 得到父組件傳遞過來的數據
data(){
return {
mymessage: this.for
}
},
});
const app = new Vue({
el: '#app',
template: `
<div>
<parent />
</div>
`
});
上面的實例中,定義了組件parent
和組件child
,組件parent
和 組件child
是父子關係。
- 在
parent
組件中,通過provide
屬性,以對象的形式向子孫組件暴露了一些屬性 - 在
child
組件中,通過inject
屬性注入了parent
組件提供的數據,實際這些通過inject
注入的屬性是掛載到 Vue 實例上的,所以在組件內部可以通過this
來訪問
注意:
官網文檔提及 provide 和 inject 主要為高階插件 / 組件庫提供用例,並不推薦直接用於應用程式程式碼中。
4.3 Vuex 狀態管理
Vuex 是狀態管理工具,實現了項目狀態的集中式管理。工具的實現借鑒了 Flux、Redux 和 The Elm Architecture 的模式和概念。當然與其他模式不同的是,Vuex 是專門為 Vue.js 設計的狀態管理庫,以利用 Vue.js 的細粒度數據響應機制來進行高效的狀態更新。