订阅发布模式实现事件总线
传统的前端框架比如Vue和React都有一套自己的通信机制,父子通信使用props,兄弟组件通信使用event bus,背后的实现原理就是发布订阅模式,所以我们可以使用上一节的小demo实现一个简单的event bus。
模拟一个使用场景
组件A和组件B为兄弟组件,且不是page(页面级别的组件,或者container),就是单纯的组件,我们在A中定义了一个方法,在B组件中调用这个方法,这个过程可以模拟一个在B组件中异步调用组件A的一个状态,代码如下:
// pattern.js
const eventObj = {
store: {},
on: (key, callback) => { // 注册
this.store[key] = !this.store[key] ? [] : this.store[key];
this.store[key].push(callback);
},
emit: (key, data) => { // 订阅
if(!this.store[key]) return false;
this.store[key].map(callback => {
return callback(data);
})
}
}
export default eventObj; // 如果没有用打包工具起项目,记得添加在package.json里babel-cli
// A 组件
eventObj.on('milkTea', ({ customer, milkName }) => { // 买奶茶啦
console.log(`${customer} 需要一杯 ${milkName}`);
});
// B 组件
setTimeout(() => {
eventObj.emit('milkTea', {customer: 'BR', milkName: '烧仙草'});
}, 2000);
这样我们就可以在B组件中异步调用A组件,似乎一切很完美,然后报错了,提示 store is not defined
,原因是const定义的变量在打包的时候会被挂在局部作用域中,全局是无法访问到的,为了让代码run起来,我们需要把pattern.js中的代码做修改:
// pattern.js
var eventObj = {
store: {},
on: (key, callback) => { // 注册
this.store[key] = !this.store[key] ? [] : this.store[key];
this.store[key].push(callback);
},
emit: (key, data) => { // 订阅
if(!this.store[key]) return false;
this.store[key].map(callback => {
return callback(data);
})
}
}
export default eventObj;
var定义的变量会在打包的过程中被挂载到window上,相当于
window.eventObj = {
···
window.eventObj.store[key] ...
···
}
同理使用let定义也是不可以的。