以自定义事件为调度中心,创建一个
EventCenter
类,默认导出一个EventCenter
实例
// 调度中心
class EventCenter {
constructor() {
// 创建一个事件中心,数据模型:{ event : [fn, fn] }
this.eventCenter = {};
}
/**
* 订阅事件
* eventName {string} 订阅事件名称
* backFn {Function} 收到通知的回调函数
**/
listen(eventName, backFn) {
// 确定是否有订阅过该事件
if (!this.eventCenter[eventName]) {
this.eventCenter[eventName] = [];
}
// 控制backFn的唯一性,防止出现重复的backFn
const backFnList = this.eventCenter[eventName];
const hasFn = backFnList.some((fn) => fn === backFn);
if (hasFn) {
return;
}
backFnList.push(backFn);
}
/**
* 取消订阅函数
* eventName {string} 取消订阅的事件名称
* backFn {Function} 取消订阅的回调函数
**/
unListen(eventName, backFn) {
// 判断是否有该事件,及该事件是否有订阅者
let backFnList = this.eventCenter[eventName];
if (!backFnList || backFnList.length === 0) {
return;
}
backFnList = backFnList.filter((fn) => fn !== backFn); // 过滤掉取消订阅的函数
}
/**
* 发布函数
* eventName {string} 发布通知的事件名称
* ...args {any} 发布通知的相关参数
**/
publish(eventName, ...args) {
// 判断是否有该事件,及该事件是否有订阅者
const backFnList = this.eventCenter[eventName];
if (!backFnList || backFnList.length === 0) {
return;
}
backFnList.forEach((backFn) => {
backFn.apply(null, args); // 调用回调函数,并传入发布时的相关参数
});
}
}
export default new EventCenter(); // 默认导出一个实例
publish
中使用了apply
是为了把参数进行转换,具体请参考此文章:《javascript 关于bind、apply、call函数改变this指向》
运用场景一: 添加商品与购物车的数量变化 ,这种情况往往是不知道跨越多少不同层级的情况。
发布者 :商品加入购物车的操作
订阅者:购物车的数量
// 商品列表 ProductList.js
import EventCenter from './EventCenter';
const ProductList = ()=>{
// 模拟商品数据
const productList = [
{id:1, name: '商品1'}
];
const addBuyCar = (product)=>{
EventCenter.publish('addBuyCar', product); // 发布通知,添加商品到购物车
}
return (
<ul>
{productList.map(product => {
return (<li key={product.id}>
商品名称:{product.name}
<button onClick={()=>addBuyCar(product)}>加入购物车</button>
</li>)
})}
</ul>
);
}
export default ProductList;
// 购物车商品种类数量 BuyCarCount.js
import {useEffect, useState} from 'react';
import EventCenter from './EventCenter';
const BuyCarCount = ()=>{
const [productList, setProductList] = useState([]); // 产品种类列表
useEffect(()=>{
// 订阅添加商品时的回调函数
const backFn = (product)=>{
setProductList(pList => {
// 判断购物车是否已存在此商品,没有才添加进去
const hasProduct = pList.some(pro => pro.id === product.id);
if(!hasProduct){
return [...pList, product];
}
return pList;
});
}
EventCenter.listen('addBuyCar', backFn); // 订阅addBuyCar事件
return ()=> EventCenter.unListen('addBuyCar', backFn); // 组件销毁后,取消订阅
}, []);
return <div>购物车商品种类:{productList.map(item=> item.name)}</div>;
}
export default BuyCarCount;