ckeditor编辑器(二)事件系统

449 阅读2分钟

1.Ckeditor4事件系统设计

ckeditor4的源码中,事件系统的模块在引入相关模块时候被注入到CKEDITOR.event。

具体的代码见 core/event.js

后续也直接把其中所有方法暴露到了CKEDITOR上

CKEDITOR.event.implementOn( CKEDITOR );

具体一共向外暴露了 on once capture fire fireOnce removeListener removeAllListeners hasListeners 这些方法,正体设计思路为 事件系统的设计 - 构造函数方式 - 发布订阅 , 其中耦合了一些些和项目业务相关的代码,就不展开了。基于其源码,我也复现了demo,如下

2.自己设计事件系统

class EventManager {
  constructor() {
    this.events = new Map();
  }
​
  on(
    eventName,
    listenerFunction,
    scopeObj = this,
    listenerData = {},
    priority = 10
  ) {
    // 获取事件,如果事件不存在则创建一个空数组
    // todo: 每个事件可以再封装一层,包含事件名,事件监听器,事件状态等
    let event = this.events.get(eventName);
    if (!event) {
      event = {
        listeners: [],
      };
      this.events.set(eventName, event);
    }
​
    // 定义监听器触发函数,接受编辑器对象、发布的数据、停止事件、取消事件等参数
    const listenerFirer = (editor, publisherData, stopFn, cancelFn) => {
      const ev = {
        name: eventName,
        sender: this,
        editor: editor,
        data: publisherData,
        listenerData: listenerData,
        // 调用时候才注册的stop和cancel方法, 在 on 内函数可以对其引用
        stop: stopFn,
        cancel: cancelFn,
        removeListener: () => {
          this.removeListener(eventName, listenerFunction);
        },
      };
​
      const ret = listenerFunction.call(scopeObj, ev);
​
      return ret === false ? EVENT_CANCELED : ev.data;
    };
​
    // 设置监听器的属性  如果该监听器没有被绑定,就添加该监听器
    listenerFirer.fn = listenerFunction;
    listenerFirer.priority = priority;
​
    const listenerIndex = event.listeners.findIndex(
      (listener) => listener.fn === listenerFunction
    );
​
    if (listenerIndex === -1) {
      event.listeners.push(listenerFirer);
​
      event.listeners.sort((a, b) => {
        return a.priority - b.priority;
      });
    }
​
    // 返回一个对象,该对象包含一个 removeListener 方法
    return {
      removeListener: () => {
        this.removeListener(eventName, listenerFunction);
      },
    };
  }
​
  removeListener(eventName, listenerFunction) {
    const event = this.events.get(eventName);
​
    if (!event) {
      return;
    }
​
    const listenerIndex = event.listeners.findIndex(
      (listener) => listener.fn === listenerFunction
    );
​
    if (listenerIndex !== -1) {
      event.listeners.splice(listenerIndex, 1);
​
      if (event.listeners.length === 0) {
        this.events.delete(eventName);
      }
    }
  }
​
  removeAllListeners() {
    this.events.clear();
  }
​
  // 触发事件
  fireEvent(eventName, editor, publisherData) {
    const event = this.events.get(eventName);
​
    if (!event) {
      return;
    }
​
    const stopFn = () => {
      this.stopEvent(eventName);
    };
​
    const cancelFn = () => {
      this.cancelEvent(eventName);
    };
​
    // 遍历所有监听器,并调用每一个监听器
    event.listeners.forEach((listener) => {
      // todo: 改成for of 循环,顺序执行通过状态控制是否继续
      listener(editor, publisherData, stopFn, cancelFn);
    });
  }
​
  stopEvent(eventName) {
    const event = this.events.get(eventName);
​
    if (!event) {
      return;
    }
​
    event.listeners.length = 0;
  }
​
  cancelEvent(eventName) {
    const event = this.events.get(eventName);
​
    if (!event) {
      return;
    }
​
    event.listeners.length = 0;
  }
}
​
const eventEmit = new EventManager();
​
// test priority
eventEmit.on(
  "test",
  (e) => {
    console.log(1, e);
  },
  eventEmit,
  {},
  100
);
eventEmit.on("test", (e) => {
  console.log(2, e);
  // 后续监听器不再执行
  // e.stop();
});
eventEmit.fireEvent("test", "editor", "publisherData");
​