故事還是得從$emit說起,某一天翻文檔的時候看到$emit的說明
觸發當前實例上的事件?就是自身組件上的事件唄,在父子組件通信中,父組件通過props傳遞給子組件數據(高階組件可以用provide和inject),由於這是單向數據流,為了保證復用組件的的時候出現數據錯亂。
那么只能通過在子組件里面去觸發一個事件,來讓父組件自己更新數據。
那么問題來了,$emit不是說好的觸發當前實例上的事件嗎?那是怎么去能觸發父組件數據更新的呢?難道父組件里也能$on對應的事件???
其實不是的,看下面這個示例
<div id="app1">
<m-area :v="value" @do="pAdd"></m-area>
</div>
<script src="js/vue.js"></script>
<script>
Vue.component('mArea', {
props: ['v'],
template: `
<div>
<button @click="add">{{v}}</button>
</div>
`,
methods: {
add() {
this.$emit('do');
console.log(this.v)
}
}
});
let app1 = new Vue({
el: '#app1',
data: {
value: 1
},
methods: {
pAdd() {
this.value++;
},
mouseover() {
console.log(this.value);
}
}
});
</script>
其實Button 的add方法是只觸發了它自身的do事件,但是
<div id="app1">
<m-area :v="value" @do="pAdd"></m-area>
</div>
mArea標簽就是該組件啊,do事件是只觸發了自身的事件,但是m-area標簽是暴露在父組件里的,看看pAdd方法,它是父組件的mthods里的一個方法。由此剛好觸發自身組件事件的時候,又觸發了父組件里的一個方法,數據可以傳參,最終達到更新父組件數據的目的。
接下來說說eventBus
一般都是創建一個bus.js,然后 export default new Vue()
其實背后就是個原理,通過bus.$emit然后涉及到原型鏈查找,相當於直接是針對於根組件App執行的$emit和$on,站在一個上帝視角,最頂層的組件上,就能形成一個事件總線,這樣不同的兄弟組件之間就能進行通信了。
順便可以看看vue-bus這個插件的源碼,其實也是這么實現的,然后是通過更改get,改了一下調用屬性方法的別名而已,然后掛到vue.prototype上,方便全局調用。
/**
* vue-bus v1.1.0
* https://github.com/yangmingshan/vue-bus
* @license MIT
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.VueBus = factory());
}(this, (function () { 'use strict';
function VueBus(Vue) {
var bus = new Vue();
Object.defineProperties(bus, {
on: {
get: function get() {
return this.$on
}
},
once: {
get: function get() {
return this.$once
}
},
off: {
get: function get() {
return this.$off
}
},
emit: {
get: function get() {
return this.$emit
}
}
});
Vue.bus = bus;
Object.defineProperty(Vue.prototype, '$bus', {
get: function get() {
return bus
}
});
}
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(VueBus);
}
return VueBus;
})));