Vue組件二-事件反饋 - 子組件向父組件發送消息,父組件監聽消息
開始
Vue組件是學習Vue框架最比較難的部分,而這部分難點我認為可以分為三個部分學習,即
所以將用三篇博客分別進行介紹以上三種情況和使用
消息監聽,消息發送
在理解Vue事件之前,可以簡單理解一下消息中心的設計模式,如下圖,即每一個訂閱者,都可以去訂閱消息。而消息會提供一個"消息名稱",訂閱者可以通過"消息名稱",訂閱特定的消息。一定訂閱者訂閱了消息,則只要發出消息,訂閱者就會被觸發。
而在Vue中,通過v-on
去訂閱一個消息,通過emit
發出一個消息。
這兩個特有的模式是v-on:message-name="someMethod"
訂閱,this.$emit("message-name")
發送一個消息。此時someMethod
會被觸發調用。
具體的實例
父組件和子組件的事件響應中,主要分為四種情況
- "v-on"/"@"綁定事件(@是對v-on的縮寫)
- 綁定原生事件
- .sync同步父組件和子組件之間的props
- 兄弟組件進行通信
"v-on"或"@"綁定事件
父組件中模版的定義
<div>
<h4>組件四-"v-on"綁定事件</h4>
<span>{{sumOfTotal}}</span>
<br />
<!--'@'是'v-on:'監聽器的簡寫-->
<component-span-child-4 v-on:increment-total="incrementWithTotal"></component-span-child-4>
<component-span-child-4 @increment-total="incrementWithTotal"></component-span-child-4>
<component-span-child-4 @increment-total="incrementWithTotal"></component-span-child-4>
</div>
子組件的定義
Vue.component("component-span-child-4", {
template: "<button v-on:click='incrementOfButtonCounter'>{{counter}}</button>",
data: function() {
return {
counter: 0
}
},
methods: {
incrementOfButtonCounter: function() {
this.counter = this.counter + 1;
// post a notification of increment counter
// 'increment-total' 相當于一個通知名稱,在父組件中,會檢測一個同名的通知名稱
this.$emit("increment-total");
}
}
})
子組件在點擊事件觸發的時候,會發送一個消息名稱為
"increment-total"
的消息,而在父組件中,訂閱了這個名稱的消息。所以父組件可以響應子組件的通知
綁定原生事件
父組件中模版的定義
<div>
<h4>組件五-綁定原生事件</h4>
<span>{{nativeSumOfTotal}}</span>
<br />
<component-span-child-5 v-on:click.native="nativeDoThing"></component-span-child-5>
</div>
子組件的定義
Vue.component("component-span-child-5", {
template: "<button>檢測原生事件-點擊</button>"
})
通過
v-on:click.native="nativeDoThing"
訂閱原生的事件。這里沒有emit
關鍵字,可以理解為這個消息是原生組件發送出的,但是訂閱還是通過v-on
.sync同步父組件和子組件之間的props
在一些情況下,我們可能會需要對一個 prop 進行『雙向綁定』。當一個子組件改變了一個 prop 的值時,這個變化也會同步到父組件中所綁定的值。這很方便,但也會導致問題,因為它破壞了『單向數據流』的假設。
父組件中模版的定義
<div>
<h4>組件六-.sync同步父組件和子組件之間的props</h4>
父組件中的值: {{food}}
<component-span-child-6 :food.sync=food></component-span-child-6>
<component-span-child-6 v-bind:food.sync=food></component-span-child-6>
<!--擴展之后的模版-->
<component-span-child-6 v-bind:food=food v-on:update:food="val => food = val"></component-span-child-6>
</div>
子組件中的定義
Vue.component("component-span-child-6", {
props: ["food"],
template: "<div>{{selectedFood}}<button v-on:click='changeSelectedFood'>點擊選擇其他食物</button></div>",
data: function() {
return {
selectedFood: this.food,
foods: ["米飯", "水果", "青菜", "沙拉"]
}
},
methods: {
changeSelectedFood: function() {
var idx = this.foods.indexOf(this.selectedFood);
if (idx == -1 || idx == this.foods.length - 1) {
idx = 0;
} else {
idx += 1;
}
this.selectedFood = this.foods[idx];
this.$emit('update:food', this.selectedFood);
}
}
})
通過父組件中國呢三種寫法(功能都是一樣的,只是由上而下,將模版擴展開寫,以窺探
.sync
的作用),其實.sync
其實會擴展出一個v-on:update:food
訂閱消息,并且在收到消息,進行了對原值的修改。
而在子組件中,依舊通過this.$emit('update:food')
發送一個消息出來
這個就是.sync
真正做了什么。
兄弟組件進行通信
兩個不是父子組件的組件如何通信,可以定一個中間總線(中介的意思),通過中間中間總線訂閱消息,中間總線發送消息,完成兩個組件之間的通信。如下
父組件模版的定義
<div>
<h4>組件七-兄弟組件進行通信</h4>
<component-span-child-7 component-name="組件7"></component-span-child-7>
<component-span-child-8 component-name="組件8"></component-span-child-8>
</div>
子組件的定義
var bus = new Vue();
Vue.component("component-span-child-7", {
props: ["componentName"],
template: "<div><span>{{componentName}}</span>:<span>{{counter}}</span></div>",
data: function() {
return {
counter: 0
}
},
mounted: function() {
// 此處在monuted階段監聽'notificationFromPartner',需要用bind方法綁定當前的this,否則回調function中的this則是bus實例,而不是當前Vue的實例
bus.$on("notificationFromPartner", function() {
this.counter += 1;
}.bind(this));
}
})
Vue.component("component-span-child-8", {
props: ["componentName"],
template: "<button v-on:click='componentClickPushMessage'>{{componentName}}</button>",
methods: {
componentClickPushMessage: function() {
bus.$emit("notificationFromPartner");
}
}
})
組件7在裝載mounted
后,通過bus.$on("notificationFromPartner", callbackFunction)
訂閱了notificationFromPartner
消息,而在組件8中,通過bus.$emit("notificationFromPartner");
發送出這個消息。則訂閱者就可以響應消息。
總結
在學習Vue中,組件作為十分重要的一個組成部分,對組件的通信的理解也十分重要。對于組件間的事件反饋,應該先理解消息中心的設計模式,則能更快的理解其中的原理。則不用糾結一些語法特性比較奇怪。不用糾結為什么v-on要和emit匹配、為什么只需要v-on就可以監聽原生native事件、為什么.sync
可以實現props的同步等一系列問題。