1. 快速認識
一句話,來說,在同一個Vue實例中,$on
、$once
用於設定監聽器, 不同的是,$once
僅會被觸發一次後立即失效。 而$off
則是一個主動的監聽器銷燬器。 $emit
則用於觸發通過$on
,$once
設定的監聽器,即事件觸發器/發射器。
以下是一個簡單的例子:
<template>
<div>
<button @click="$emit('cusEvent', 'cusEvent trigger~')">Emit cusEvent</button>
<button @click="$off('cusEvent')">destory cusEvent</button>
<br>
<button @click="$emit('cusOnceEvent','only once chance to trigger~~~')">Emit OnceEvent</button>
</div>
</template>
<script>
export default {
created() {
this.$on("cusEvent", (msg) => {
console.log(msg,'--line13');
});
this.$once("cusOnceEvent",(msg)=>{
console.log(msg,'--line20');
})
},
};
</script>
在生命週期created
鉤子中,定義了兩個事件監聽器,分別是$on
監聽的cusEvent
和 $once
監聽的cusOnceEvent
.從演示中看到,執行操作的結果符合我們的預期,$once
和$on
都可以通過$emit
觸發,此外,$once
僅觸發一次,且$on
在被$off
銷燬後,不再觸發。
2. 進一步認識, 使用方法
2.1 vm.$on( event, callback )
參數:
{string | Array<string>} event
(數組只在 2.2.0+ 中支持){Function} callback
用法:
監聽當前實例上的自定義事件。事件可以由
vm.$emit
觸發。回調函數會接收所有傳入事件觸發函數的額外參數。
傳入的監聽函數,並不是只能是單個單個註冊,可以是一個數組,以$on
爲例:
<template>
<div>
<button @click="$emit('cusE01','E01')">cusE01</button>
<button @click="$emit('cusE02','E02','hello')">cusE02</button>
<button @click="$emit('cusE03','E03','hello','world')">cusE03</button>
</div>
</template>
<script>
export default {
created(){
this.$on(['cusE01','cusE02','cusE03'],(...params)=>{
console.log(params,'--line12');
// ['E01'] '--line12'
// ['E02', 'hello'] '--line12'
// ['E03', 'hello', 'world'] '--line12'
})
}
};
</script>
<style></style>
2.2 vm.$once( event, callback )
參數:
{string} event
{Function} callback
用法:
監聽一個自定義事件,但是隻觸發一次。一旦觸發之後,監聽器就會被移除。
注意,$once
接收參數僅可爲一個,不可爲數組,若爲數組,在觸發任意一個監聽器後,全部即刻失效。
如下示例:
<template>
<div>
<button @click="$emit('cusOnceE01','OnceE01')">cusOnceE01</button>
<button @click="$emit('cusOnceE02','OnceE02','hello')">cusOnceE02</button>
<button @click="$emit('cusOnceE03','OnceE03','hello','world')">cusOnceE03</button>
</div>
</template>
<script>
export default {
created(){
this.$once(['cusOnceE01','cusOnceE02','cusOnceE03'],(...params)=>{
console.log(params,'--line12');
// 以下輸出僅在相應按鈕點擊時觸發一次,立刻失效,即以下輸出只能單次輸出其中一個
// ['OnceE01'] '--line12'
// 或
// ['OnceE02', 'hello'] '--line12'
// 或
// ['OnceE03', 'hello', 'world'] '--line12'
})
}
};
</script>
<style></style>
2.3 vm.$off( [event, callback] )
參數:
{string | Array<string>} event
(只在 2.2.2+ 支持數組){Function} [callback]
用法:
移除自定義事件監聽器。
- 如果沒有提供參數,則移除所有的事件監聽器;
- 如果只提供了事件,則移除該事件所有的監聽器;
- 如果同時提供了事件與回調,則只移除這個回調的監聽器。
2.3.1 同時銷燬多個監聽器
指定銷燬:當$off
傳入一個包含監聽器事件名的數組作爲第一個參數時,將可以指定的銷燬當前實例多個定時器。
全部銷燬:當$off
不傳入任何參數時,將移除當前實例的所有監聽器。
<template>
<div>
<button @click="$emit('cusE01','E01')">cusE01</button>
<button @click="$emit('cusE02','E02','hello')">cusE02</button>
<button @click="$emit('cusE03','E03','hello','world')">cusE03</button>
<button @click="$off(['cusE01','cusE02'])">Off Multi</button><!--指定銷燬/移除多個-->
<button @click="$off()">Off All</button><!--全部銷燬/移除-->
</div>
</template>
<script>
export default {
created(){
this.$on(['cusE01','cusE02','cusE03'],(...params)=>{
console.log(params,'--line12');
// ['E01'] '--line12'
// ['E02', 'hello'] '--line12'
// ['E03', 'hello', 'world'] '--line12'
})
}
};
</script>
2.4 vm.$emit( eventName, […args] )
參數:
{string} eventName
[...args]
觸發當前實例上的事件。附加參數都會傳給監聽器回調。
注意,同時只能觸發一個監聽器事件。
3. 兩個組件傳值可通過事件總線EventBus實現究竟是怎麼回事?
分析:
首先,我們知道,$on
,$once
,$emit
等等這些是"實例事件/方法",也就是說,他們是Vue實例上的自帶方法。 我們在<script></script>
標籤包裹的部分去使用這些方法時,前面需要帶上this
才能夠正常訪問到。 就像這樣this.$on
, this.$emit
...., 我們也知道,this
通常,指向的就是Vue
實例對象。
有了這些認識,我們能不能這樣做,我們創建一個Vue實例,然後在A組件中去動態的給這個實例"埋設"一個監聽器, 然後再在B組件中去動態觸發這些監聽器呢?
實際,這就是Vue EventBus 事件總線的核心思路。 它實際上很簡潔。下面是一個實現的示例:
// bus.js 實例化一個vue對象,並導出:
import Vue from 'vue'
export default new Vue();
再然後再在 A,B 組件中分別去導入:
<script>
import Bus from './bus'
....
A, B 組件分別時這樣去定義的:
<!-- A -->
<template>
<div>
<p>Child A</p>
<button @click="trigger">emit cusEvent</button>
</div>
</template>
<script>
import Bus from './bus'
export default {
data() {
return {
name: "jayce",
};
},
methods:{
trigger(){
Bus.$emit('cusEvent',this.name)
}
}
};
</script>
<!-- B -->
<template>
<p>Child B</p>
</template>
<script>
import Bus from './bus'
export default {
created(){
Bus.$on('cusEvent',(par)=>{
console.log(par,'--line7');
})
}
}
</script>
現在你就能在A組件中通過一個點擊事件,將name
這個變量通過自定義事件傳參的方式從A傳遞到B。
簡單的描述一下:
我們在B組件中,週期函數created
中,通過Bus.$on
給bus.js "埋設"了一個自定義事件,名爲"cusEvent", 且定義了一個形參par
在其回調函數中。
緊接着,我們在A組件中,通過點擊事件觸發了bus.js 中埋設的事件"cusEvent", 並傳入了一個實參name
。
這樣,知道點擊了A組件中的該按鈕,就會觸發我們的自定義事件,然後通過回調函數的參數接收到“傳參”。
可以簡單總結下這個示例:
- "發送參數" 通過
$emit
觸發監聽器實現,"接受參數"通過$on
監聽事件被觸發後回調所傳入的值得到。 - 你會發現,實際上事件總線,只和兩個單獨的組件有關,所以理論上,你可以實現任意兩個組件之間的傳值。而不僅僅是兄弟傳值。
【注意事項】:
在該示例中,A組件中,去觸發的時候,不能這樣去寫:
<template><div><p>Child A</p><button @click="Bus.$emit('cusEvent',name)">emit cusEvent</button></div></template><script>import Bus from './bus'export default {data() {return { name: "jayce",};},};</script>
直接把點擊事件觸發內容定義在Dom上是不可以的,會報錯誤找不到
Bus
, 而出現這個問題的原因是因爲Vue組件的渲染週期導致的。 Vue 組件的dom渲染是異步執行的。 直到mounted
才完成渲染,有可能Bus還沒有被import
進來,就渲染解析到了dom上的Bus.$emit....
所以,就找不到,會報錯。我們將點擊事件觸發內容定義在methods中,dom在渲染時,就只會找methods中存不存在對應的方法,在上例中,就是
trigger
這個方法。