在Vue中采用了发布订阅模式,典型的兄弟组件间的通信
$on
和$emit
发布订阅模式:(订阅者、发布者、信号中心)
一个发布者
$emit
发布一个事件到信号中心eventBus
,订阅者们$on
通过信号中心收到该事件,进行处理。
Vue.prototype.$emit
的作用是循环执行当前 vm
(组件实例)的 _events
属性内某个 event
(事件名)对应的事件 回调 列表。也就是触发事件。
来看看源码,以下代码来自 vue@2.6.14.global.js
Vue.prototype.$emit = function(event) {
var vm = this;
{
var lowerCaseEvent = event.toLowerCase();
//...
}
var cbs = vm._events[event];
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs;
var args = toArray(arguments, 1);
var info = "event handler for \"" + event + "\"";
for (var i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info);
}
}
return vm
};
而 vm
的 _events
属性内的对 event
的回调方法收集全部是通过Vue.prototype.$on
方法收集的。即事件监听。
Vue.prototype.$on = function(event, fn) {
var vm = this;
if (Array.isArray(event)) {
for (var i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn);
}
} else {
(vm._events[event] || (vm._events[event] = [])).push(fn);
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true;
}
}
return vm
};
通过 $emit
实现父子组件通讯的第一个步骤:在父组件内,对子组件的占位符标签上绑定一个自定义事件回调。根据传入的事件名从当前实例的_events
属性(即事件中心)中获取到该事件名所对应的回调函数cbs
,然后再获取传入的附加参数args
,由于cbs
是一个数组,所以遍历该数组,拿到每一个回调函数,执行回调函数并将附加参数args
传给该回调。这个绑定动作最终将通过子组件的 $on
方法将回调进行收集。
在这里模拟一个自定义事件 $on和$emit
事件
//utils 新建里 event.js
export default{
map:{},
$emit(name,params){
if(this.map[name]==null){
console.log("没有找到关于"+name+"的事件,无法触发")
}else{
this.map[name].detail=params;
window.dispatchEvent(this.map[name]);
}
},
$on(name,work){
let myEvent = new Event(name);
this.map[name]=myEvent;
window.addEventListener(name, (event)=>{
console.log('得到数据为:', event.detail);
work(this.map[name].detail);
});
}
}
在main.js
中注入(Vue3中使用eventBus.$on
、eventBus.$emit
功能)
import { createApp } from 'vue'
import App from './App.vue'
import myEvent from './utils/event.js';//引入
const app = createApp(App);
app.config.globalProperties.$event = myEvent;//绑定到全局中
app.mount('#app');
vue3中使用
import { getCurrentInstance, onMounted } from "vue";
const { proxy} =getCurrentInstance();
const $event=proxy.$event;
//以上为公共,每个页面都需要注册;
//child页面
const sendData=()=>{
$event.$emit("changeStartMenu",11111)
}
//根页面
onMounted(()=>{
$event.$on("changeStartMenu",(res)=>{
console.log(res,"触发成功")
})
})
补充:
getCurrentInstance
只能在setup
或 生命周期钩子 中调用。如需在
setup
或 生命周期钩子 外使用,请先在setup
中调用getCurrentInstance()
获取该实例后再使用。