众所周知,在我们现代开发中必不可少要用到Vue或者React, 那么我们一般父子通信一般会用Props, 虽然官方也会说两个同级兄弟组件你可以传到同级父组件然后分发,但是这样的效率实在令人捉急,层级一多你就蛋疼了(react官方出的context也是为了解决此类问题),既然都是单页应用,我们搞个事件监听和传递不就行了

在以前就有

element.addEventListener('click;', function(){})

首先我们需要借助发布订阅实现几个关键api,on / off / emit
他的原理也相当容易理解,我们维护一个事件队列,如果我们用一个key去增加/触发事件函数获得所需要的值,这样就完成了我们跨组件数据传递的需求

Event.js

class EventEmitter {
    constructor() {
        this.handersArr = {}
    }
    on(eventType, handle) {
        this.handersArr[eventType] = this.handersArr[eventType] || []
        this.handersArr[eventType].push(handle)
    }

    emit(eventType, ...args) {
        if (Array.isArray(this.handersArr[eventType])) {
            this.handersArr[eventType].forEach((item) => {
                item(...args)
            })
        }
    }

    off(eventType) {
        this.handersArr[eventType] && delete this.handersArr[eventType]
    }
}

export default EventEmitter

当然这样我们就完成了超级简单的事件管理器, 但是这里我们想把它用在vue上会需要做这么几个事情,在created接收其他组件的事件(如果有),同比react就是componentDidMount 然后在destoryed阶段(同比react就是componentWillUnmount)把这个事件摧毁,嗯,如果每次这么做就有点不够先进,而且和其他业务代码揉合在一起,不科学

那我们可以在Event.js搞点事情,

    bindVue(handlers) {
        const _this = this
        let handlersWithVue = {}
        return {
            created() {
               for (const [event, _handler] of Object.entries(handlers)) {
                   const handler = _handler.bind(this) // 把_handler的this绑定到vue调用环境中去
                   _this.on(event, handler)
                   handlersWithVue[event] =  handler
               } 
            },

            beforeDestroy() {
                _this.off(handlersWithVue)
            }
        }
    }

这里涉及到一个奇技淫巧,vue的mixins特征,简单暴力来说,它可以把你需要定制的vue的js部分的逻辑全部塞进你的业务.js里面,并起到作用,咦...这上面说的不就成了。

import EventBus from '../util/bus.js'
export default {
    mixins: [EventBus.bindVue({
         changeText(value) {  // 这里就是on方法啦
              this.msg = value
         }
    })],


   [emit:]  EventBus.emit({changeText: 666})

当然你会问React怎么实现,因为react真的是class,所以你拿到component真的就是标准对象,所以react的处理更方便点,直接把我们那个bindVue换成这个就成,react建议在constructor里面on监听(其实我们项目还有个hooks的实现23333),把我们需要的事件在componentDidMount执行,componentWillUnmount销毁

    bindComponent(component, handlers) {
        const didMount = component.componentDidMount
        const willUnmount = component.componentWillUnmount

        component.componentDidMount = () => {
            this.on(handlers)
            if (didMount) {
                return didMount.apply(component)
            }
        }

        component.componentWillUnmount = () => {
            this.off(handlers)
            if (willUnmount) {
                return willUnmount.apply(component)
            }
        }
    }

其实这里正好运用了JavaScript事件驱动和单线程的特点,比较复杂的是,你要注意this的指向,这里确实还是挺乱的,一不小心就不知道操作的是谁的this了。

preView