ckeditor5

ckeditor5/utils:emittermixin(事件监

2020-09-23  本文已影响0人  videring
ckeditor5/utils:emittermixin主要负责事件绑定、触发和代理用。主要方法有listenTo/stopListeningfiredelegate/stopDelegating。一般而言,EmitterMixin会extend到ObservableMixin,而ObservableMixin最后会mix到某个class:
// class
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
import mix from '@ckeditor/ckeditor5-utils/src/mix';

class AnyClass {
    // ...
}

mix( AnyClass, ObservableMixin );
// observableMixin.js
import { extend, isObject } from 'lodash-es';
extend( ObservableMixin, EmitterMixin );

零、_listeningTo和_emitterId

emittermixin有两个常量_listeningTo_emitterId,作用如下:

// contains a list of emitters that this object is listening to.
// This list has the following format:
// _listeningTo: {
//     emitterId: {
//         emitter: emitter,
//         callbacks: {
//             event1: [ callback1, callback2, ... ]
//             ....
//         }
//     },
//     ...
// }
const _listeningTo = Symbol( 'listeningTo' );
// emitter's unique id which can be set only once.
const _emitterId = Symbol( 'emitterId' );

一、listenTo( emitter, event, callback, options = {} )

onceon方法依赖listenTo方法,所以只需知道listenTo的实现即可。

    if ( !( emitterInfo = emitters[ emitterId ] ) ) {
        emitterInfo = emitters[ emitterId ] = {
            emitter,
            callbacks: {}
        };
    }
    if ( !( eventCallbacks = emitterInfo.callbacks[ event ] ) ) {
            eventCallbacks = emitterInfo.callbacks[ event ] = [];
    }

二、stopListening( emitter, event, callback )

off依赖stopListening,所以只需知道stopListening的实现即可。这个功能相对简单:

三、delegate

先看一个test用例(详见tests/emittermixin.js):

it( 'supports delegation under a different name', () => {
                const emitterA = getEmitterInstance();
                const emitterB = getEmitterInstance();
                const emitterC = getEmitterInstance();
                const emitterD = getEmitterInstance();
                const spyAFoo = sinon.spy();
                const spyABar = sinon.spy();
                const spyCBaz = sinon.spy();
                const spyDFoo = sinon.spy();

                emitterB.delegate( 'foo' ).to( emitterA, 'bar' );
                emitterB.delegate( 'foo' ).to( emitterC, name => name + '-baz' );
                emitterB.delegate( 'foo' ).to( emitterD );

                emitterA.on( 'foo', spyAFoo );
                emitterA.on( 'bar', spyABar );
                emitterC.on( 'foo-baz', spyCBaz );
                emitterD.on( 'foo', spyDFoo );

                emitterB.fire( 'foo' );

                sinon.assert.calledOnce( spyABar );
                sinon.assert.calledOnce( spyCBaz );
                sinon.assert.calledOnce( spyDFoo );
                sinon.assert.notCalled( spyAFoo );
} );

delegate方法会返回一个包含to方法的对象:

delegate( ...events ) {
        return {
            to: ( emitter, nameOrFunction ) => {
                if ( !this._delegations ) {
                    this._delegations = new Map();
                }

                // Originally there was a for..of loop which unfortunately caused an error in Babel that didn't allow
                // build an application. See: https://github.com/ckeditor/ckeditor5-react/issues/40.
                events.forEach( eventName => {
                    const destinations = this._delegations.get( eventName );

                    if ( !destinations ) {
                        this._delegations.set( eventName, new Map( [ [ emitter, nameOrFunction ] ] ) );
                    } else {
                        destinations.set( emitter, nameOrFunction );
                    }
                } );
            }
        };
},

实现也很简单,主要是要往this._delegations中保存类似{${event} : { ${emitter}: ${nameOrFunction} }}的结构,会在stopDelegating中用到。

四、stopDelegating

stopDelegating( event, emitter ) {
        if ( !this._delegations ) {
            return;
        }

        if ( !event ) {
            this._delegations.clear();
        } else if ( !emitter ) {
            this._delegations.delete( event );
        } else {
            const destinations = this._delegations.get( event );

            if ( destinations ) {
                destinations.delete( emitter );
            }
        }
}

五、fire( eventOrInfo, ...args )

if ( this._delegations ) {
                const destinations = this._delegations.get( event );
                // emitterB.delegate( '*' ).to( emitterA ); 全部代理,emitterB上fire任意时间,emitterA的on方法的回调函数都是触发。
                const passAllDestinations = this._delegations.get( '*' ); 

                if ( destinations ) {
                    fireDelegatedEvents( destinations, eventInfo, args );
                }

                if ( passAllDestinations ) {
                    fireDelegatedEvents( passAllDestinations, eventInfo, args );
                }
}
return eventInfo.return;
上一篇下一篇

猜你喜欢

热点阅读