vue組件之間通訊總結(超詳細)
組件通訊在我們平時開發過程中,特別是在vue和在react中,有著舉足輕重的地位。本篇將總結在vue中,組件之間通訊的幾種方式:
- props、$emit
- $parent、$children
- $attrs、$listeners
- provide、inject
- eventBus
- vuex
- 本地存儲
一、props、$emit單向數據流
father.vue:
<template> <div> <div>我是父親:<input type="button" value="父親" /> 數字為: {{num}}</div> <son :num="num" @change="change"></son> </div> </template> <script> import son from "./son.vue"; export default { name: "Father", components: { son, }, data() { return { num: 1, }; }, methods:{ change(val){ this.num = val } } }; </script>
son.vue:
<template> <div>我是兒子:<input type="button" value="兒子" @click="change"/>數字為:{{num}}</div> </template> <script> export default { name: "App", components: {}, props: { num: { default: 0, }, }, created() {}, methods: { change(){ // this.num = 2 props通訊是單向數據流,在這直接修改父組件傳過來的num將會幫錯
// 可以用$emit傳遞change事件,father組件綁定change事件
this.$emit('change', 2)
} }, }; </script>
對於上面的場景:子組件的change事件只是為了修改父組件中某一個值,還可以有以下幾種寫法:
1.父組件綁定給子組件的事件使用箭頭函數
father: <son :num="num" @change="val => num = val"></son> son: this.$emit('change', 2)
2.update:num和.sync
father: <son :num.sync="num"></son> son: this.$emit('update:num', 2)//update是規定的寫法,不可更換
3.v-model
先修改props和綁定的事件:
father:
<son :value="num" @input="val => num = val"></son>
son:
this.$emit('input', 2)
可用v-model簡寫:
<son v-model="num"></son>
二、$parent、$children
$parent、$children可直接在父子組件中調用各自的方法以及修改數據
子組件中直接:this.$parent.num = 2
父組件中需要用ref來選中指定的子組件
vue官方並不推薦使用這種通訊方式:節制地使用 $parent
和 $children
– 它們的主要目的是作為訪問組件的應急方法,更推薦用 props 和 events 實現父子組件通訊。
三、$attrs、$listeners
$attrs可以拿到父組件傳過來的屬性:
<div>我是兒子:<input type="button" value="兒子" @click="change"/>數字為:{{$attrs}}</div>
dom節點:
$attrs會直接將傳過來的屬性放到對應的標籤上,反觀props就不會。如果想去掉標籤中的這些屬性,可以用inheritAttrs:
值得注意的是:props的優先順序大於$attrs,即當props存在的時候,$attrs為空對象:
$attrs常用於跨多級組件傳遞屬性,比如祖孫組件,用父組件做中轉:
father:
<son v-bind="$attrs"></son>
$attrs用於屬性跨級傳遞,方法跨級傳遞則用$listeners。
grandFather.vue:
<template> <div> <div>我是祖父: 數字為:{{nums}}</div> <father :nums="nums" @up="up" @down="down"></father>
</div> </template> <script> import father from "./father.vue"; export default { name: "App", components: { father, }, data(){ return { nums:0 } }, methods: { up() { alert('up') },
down() {
alert('down')
}, }, }; </script>
father.vue:
<son v-bind="$attrs" v-on="$listeners"></son>
son.vue:
<div>我是兒子:<input type="button" value="兒子" @click="$listeners.up"/></div>
四、provide、inject
這對選項需要一起使用,以允許一個祖先組件向其所有子孫後代注入一個依賴,不論組件層次有多深,並在其上下游關係成立的時間裡始終生效
provide選項應該是一個對象或返回一個對象的函數。
inject選項應該是一個字元串數組或一個對象。
App:
... export default { provide(){ return {vm: this} }, ...
son:
... export default { inject: ['vm'],
data(){},
mounted(){
console.log(this.vm)
} ...
注意:provide 和 inject 綁定並不是可響應的。這是刻意為之的。然而,如果你傳入了一個可監聽的對象,那麼其對象的 property 還是可響應的。
inject注入中的值會沿著組件向上查找,遵從”就近原則”。
provide 和 inject中的數據流是雙向的。
五、eventBus(事件匯流排)
eventBus通過發布訂閱全局事件,供其他組件使用。
在main.js中:
Vue.prototype.$bus = new Vue();
parent.vue:
<template> <div> <son1></son1> <son2></son2> </div> </template> <script> import son1 from './son1.vue' import son2 from './son2.vue' export default { name: 'parent', components: { son1, son2 }, created(){ this.$bus.$on('busEvent',(v)=>{ console.log(v); }) }, beforeDestroy(){ this.$bus.off('busEvent') } } </script>
son1和son2中的mounted:
son1:
mounted(){ this.$bus.$emit('busEvent','son1哈哈') }
son2:
mounted(){
this.$bus.$emit('busEvent', 'son2嘻嘻')
}
列印結果:
使用eventBus有三點需要注意,1.$bus.on應該在created鉤子內使用,如果在mounted使用,它可能接收不到其他組件來自created鉤子內發出的事件;
2.$bus.emit應該在mounted中使用,等待created中的$bus.on事件綁定完成;
3.發布訂閱的事件在beforeDestory鉤子里需要使用$bus.off解除,組件銷毀後沒必要一直監聽。
接下來還有vuex和storage實現組件通訊,明天補上~
腳踏實地行,海闊天空飛~