EventPluginHub

react的事件机制区别于浏览器原生的在节点上绑定回调函数的机制。react将绑定函数以组件实例为属性存入缓存linstenerBank中,事件触发时构建合成事件对象event,取出linstenerBank中存储的绑定函数,并赋值给event._dispatchListeners,随后取出该回调函数并执行。只有处理特殊的兼容性问题时,react才会给dom节点绑定具体的回调函数。

 

EventPluginHub模块用于存储绑定回调函数、构建合成事件对象(同时向合成事件对象注入绑定回调函数)、触发绑定回调函数的执行。

 

'use strict';

var _prodInvariant = require('./reactProdInvariant');

var EventPluginRegistry = require('./EventPluginRegistry');

// 为合成事件对象添加绑定的回调函数及关联的组件实例,或者获取绑定的回调函数并执行
var EventPluginUtils = require('./EventPluginUtils');

// 用于try-catch形式执行某个约定参数的回调函数
var ReactErrorUtils = require('./ReactErrorUtils');

// 复合数组或元素,构成新的数组后返回
var accumulateInto = require('./accumulateInto');

// 遍历数组,执行某个函数
var forEachAccumulated = require('./forEachAccumulated');

var invariant = require('fbjs/lib/invariant');

// 以{eventName:{ ['.'+inst._rootNodeID]: listener }}形式存储回调函数
var listenerBank = {};

// 数组形式缓存合成事件对象
var eventQueue = null;

// 冒泡及捕获方式执行已添加到合成事件对象event中的绑定回调函数
// simulated为真值启用try-catch语句;为否值且是开发环境时,创建虚拟节点和虚拟事件触发执行绑定的回调函数_dispatchListeners
var executeDispatchesAndRelease = function (event, simulated) {
  if (event) {
    // 以冒泡及捕获方式执行绑定的回调函数,执行完成后清空event的currentTarget、_dispatchListeners、_dispatchInstances属性
    EventPluginUtils.executeDispatchesInOrder(event, simulated);

    if (!event.isPersistent()) {
      event.constructor.release(event);
    }
  }
};

// 开发环境下,创建虚拟节点和虚拟事件、将绑定的回调函数作为虚拟事件的回调,以此冒泡及捕获执行绑定的回调函数
// 生产环境使用try-catch形式执行已添加到合成事件对象event中的绑定回调函数
var executeDispatchesAndReleaseSimulated = function (e) {
  return executeDispatchesAndRelease(e, true);
};

// 以try-catch形式、冒泡及捕获方式执行已添加到合成事件对象event中的绑定回调函数
var executeDispatchesAndReleaseTopLevel = function (e) {
  return executeDispatchesAndRelease(e, false);
};

var getDictionaryKey = function (inst) {
  // Prevents V8 performance issue:
  // https://github.com/facebook/react/pull/7232
  return '.' + inst._rootNodeID;// ReactCompositeComponent、ReactDomComponent实例均有_rootNodeID属性
};

// 判断是否交互型元素
function isInteractive(tag) {
  return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea';
}

// ReactDomComponent组件元素是交互型元素,同时设置props.disabled为true
function shouldPreventMouseEvent(name, type, props) {
  switch (name) {
    case 'onClick':
    case 'onClickCapture':
    case 'onDoubleClick':
    case 'onDoubleClickCapture':
    case 'onMouseDown':
    case 'onMouseDownCapture':
    case 'onMouseMove':
    case 'onMouseMoveCapture':
    case 'onMouseUp':
    case 'onMouseUpCapture':
      return !!(props.disabled && isInteractive(type));
    default:
      return false;
  }
}

// react事件机制不是将回调函数绑定在dom节点上,而是通过触发事件的dom节点获取组件实例和绑定的回调函数
// 并将该这批组件实例和绑定回调函数注入到合成事件对象当中,随后取出执行
// 针对特殊的兼容性问题,才调用PluginModule.didPutListener方法为dom节点绑定具体的回调函数

// EventPluginHub模块的putListener方法用于存储实例的绑定回调函数
// extractEvents方法用于构建合成事件对象,并向该对象注入绑定的回调函数及其相应的组件实例
// processEventQueue取出合成事件对象的系列绑定回调函数并执行
var EventPluginHub = {

  // 向EventPluginRegistry模块注入事件插件模块
  // ReactDefaultInjection模块加载的SimpleEventPlugin、EnterLeaveEventPlugin、ChangeEventPlugin、SelectEventPlugin、BeforeInputEventPlugin模块
  injection: {
    injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder,
    injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName

  },

  // 将回调函数存储到listenerBank中,事件触发时取出作为合成事件对象的_dispatchListeners属性
  // 同时调用PluginModule.didPutListener处理兼容性问题
  putListener: function (inst, registrationName, listener) {
    !(typeof listener === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Expected %s listener to be a function, instead got type %s', registrationName, typeof listener) : _prodInvariant('94', registrationName, typeof listener) : void 0;

    var key = getDictionaryKey(inst);
    var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {});
    bankForRegistrationName[key] = listener;

    // 处理兼容性问题,如SimpleEventPlugin模块处理手机端Safari非交互节点不绑定回调函数的问题
    var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
    if (PluginModule && PluginModule.didPutListener) {
      PluginModule.didPutListener(inst, registrationName, listener);
    }
  },

  // EventPluginUtils模块中使用
  // 事件触发时,取出存储在listenerBank中的绑定回调函数,添加到合成事件对象的_dispatchListeners属性中
  getListener: function (inst, registrationName) {
    // ReactDomComponent组件实例为交互型节点,同时props.disabled属性为真,不获取绑定回调函数
    var bankForRegistrationName = listenerBank[registrationName];
    if (shouldPreventMouseEvent(registrationName, inst._currentElement.type, inst._currentElement.props)) {
      return null;
    }
    var key = getDictionaryKey(inst);
    return bankForRegistrationName && bankForRegistrationName[key];
  },

  // 删除listenerBank中实例inst、事件名registrationName的相关绑定回调函数
  // 同时调用PluginModule.willDeleteListener解绑兼容性处理类事件
  deleteListener: function (inst, registrationName) {
    var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
    if (PluginModule && PluginModule.willDeleteListener) {
      PluginModule.willDeleteListener(inst, registrationName);
    }

    var bankForRegistrationName = listenerBank[registrationName];
    if (bankForRegistrationName) {
      var key = getDictionaryKey(inst);
      delete bankForRegistrationName[key];
    }
  },

  // 删除listenerBank中所有绑定回调函数
  // 同时调用PluginModule.willDeleteListener解绑兼容性处理类事件
  deleteAllListeners: function (inst) {
    var key = getDictionaryKey(inst);
    for (var registrationName in listenerBank) {
      if (!listenerBank.hasOwnProperty(registrationName)) {
        continue;
      }

      if (!listenerBank[registrationName][key]) {
        continue;
      }

      var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
      if (PluginModule && PluginModule.willDeleteListener) {
        PluginModule.willDeleteListener(inst, registrationName);
      }

      delete listenerBank[registrationName][key];
    }
  },

  // 数组形式获取合成事件对象,存储有事件名、绑定回调函数及相关组件实例
  extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
    var events;

    // 获取各类事件插件模块,ReactDefaultInjection模块加载的SimpleEventPlugin、EnterLeaveEventPlugin、ChangeEventPlugin、SelectEventPlugin、BeforeInputEventPlugin模块
    // 该插件模块内含extractEvents方法用于提取对应事件类型的合成事件对象
    var plugins = EventPluginRegistry.plugins;

    for (var i = 0; i < plugins.length; i++) {
      // Not every plugin in the ordering may be loaded at runtime.
      var possiblePlugin = plugins[i];
      if (possiblePlugin) {

        // 获取合成事件对象,该对象的dispatchConfig记录事件名,用以获取组件实例的绑定回调函数
        // _dispatchListeners属性记录绑定的会回调函数,_dispatchInstances记录关联的react组件实例
        var extractedEvents = possiblePlugin.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
        
        // 将获取到的合成事件对象extractedEvents添加到events中
        if (extractedEvents) {
          events = accumulateInto(events, extractedEvents);
        }
      }
    }
    return events;
  },

  // 将合成事件对象存储到eventQueue缓存中
  enqueueEvents: function (events) {
    if (events) {
      eventQueue = accumulateInto(eventQueue, events);
    }
  },

  // 派发事件,即冒泡、捕获执行绑定的回调函数
  processEventQueue: function (simulated) {
    // Set `eventQueue` to null before processing it so that we can tell if more
    // events get enqueued while processing.
    var processingEventQueue = eventQueue;
    eventQueue = null;

    // 执行processingEventQueue中每个合成事件对象的绑定回调函数
    if (simulated) {
      forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseSimulated);
    } else {
      forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
    }

    !!eventQueue ? 
      process.env.NODE_ENV !== 'production' ? 
        invariant(false, 'processEventQueue():' 
          + ' Additional events were enqueued while processing an event queue.' 
          + ' Support for this has not yet been implemented.') 
        : _prodInvariant('95') 
      : void 0;

    // 抛出绑定回调函数执行过程中捕获到的错误
    ReactErrorUtils.rethrowCaughtError();
  },

  // 测试使用,清空listenerBank中存储的事件回调函数
  __purge: function () {
    listenerBank = {};
  },

  // 测试使用,获取存储有事件回调函数的listenerBank
  __getListenerBank: function () {
    return listenerBank;
  }
};

module.exports = EventPluginHub;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值