React事件机制-之-事件初始化

777 阅读3分钟

事件初始化

react事件系统: 为了实现event夸平台, react实现了自己的事件系统;

文件加载-事件初始化

在引入react-dom时候就执行了事件模块的初始化; 在ReactDOM.js文件中,这句代码:import './ReactDOMClientInjection'意味着代码初始化开始了;

在ReactDOMClientInjection文件

调用了两个重要的方法:

  • injectEventPluginOrder
  • injectEventPluginsByName

上述两个方法来自于legacy-events/EventPluginRegistry

整个注册过程就是为了设置这些变量,这些变量是合成事件和原生事件的对应关系、plugin的对应关系;这些变量在后续的 DOM 操作中会扮演比较重要的角色。

// eventNameDispatchConfigs 
{
  change: ChangeEventPlugin.eventTypes.change,
  ...
}
// registrationNameModules
{
  onChange: ChangePlugin,
  onChangeCapture: ChangePlugin
}
// registrationNameDependencies
{
  onChange: ChangePlugin.eventTypes.change.dependencies,
  onChangeCapture: ChangePlugin.eventTypes.change.dependencies
}
// plugins
plugins = [SimpleEventPlugin, EnterLeaveEventPlugin, ChangeEventPlugin, SelectEventPlugin, BeforeInputEventPlugin];

详细代码执行情况(如有需要就继续往下看)

const DOMEventPluginOrder = [
  'ResponderEventPlugin',
  'SimpleEventPlugin',
  'EnterLeaveEventPlugin',
  'ChangeEventPlugin',
  'SelectEventPlugin',
  'BeforeInputEventPlugin',
];

injectEventPluginOrder作用是复制DOMEventPluginOrder, 存放于eventPluginOrder

export function injectEventPluginOrder(
  injectedEventPluginOrder,
) {
  // 克隆顺序,使其无法动态更改。
  eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
  recomputePluginOrdering();
}

injectEventPluginsByName: 挂载每一个plugin与namesToPlugins; 并调用recomputePluginOrdering——核心方法

// plugins有5种:
import BeforeInputEventPlugin from '../events/BeforeInputEventPlugin';
import ChangeEventPlugin from '../events/ChangeEventPlugin';
import EnterLeaveEventPlugin from '../events/EnterLeaveEventPlugin';
import SelectEventPlugin from '../events/SelectEventPlugin';
import SimpleEventPlugin from '../events/SimpleEventPlugin';
export function injectEventPluginsByName(
  injectedNamesToPlugins
){
  let isOrderingDirty = false;
  for (const pluginName in injectedNamesToPlugins) {
    if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
      continue;
    }
    const pluginModule = injectedNamesToPlugins[pluginName];
    if (
      !namesToPlugins.hasOwnProperty(pluginName) ||
      namesToPlugins[pluginName] !== pluginModule
    ) {
      namesToPlugins[pluginName] = pluginModule;
      isOrderingDirty = true;
    }
  }
  if (isOrderingDirty) {
    recomputePluginOrdering();
  }
}

eventTypes是以原生事件为key的map对象,map中的phasedRegistrationNames是组件props的名字如onChange; dependencies是如果需要绑定change事件需要同时绑定哪些事件;

extractEvents是一个方法,用来根据具体真实触发的事件类型等参数,返回对应的事件对象,也可以返回null表示当前事件跟这个插件没有关系。

  // plugin构成
  const ChangeEventPlugin = {
    eventTypes,
    extractEvents: function(
      topLevelType,
      targetInst,
      nativeEvent,
      nativeEventTarget,
    ){}
  }
  // eventTypes [key: string] 为原生事件
  const eventTypes = {
    change: {
      phasedRegistrationNames: {
        bubbled: 'onChange',
        captured: 'onChangeCapture',
      },
      _isInputEventSupported, // ?作用是啥
      dependencies: [
        TOP_BLUR, // 原生事件
      ]
    }
  }

函数recomputePluginOrdering为核心函数:

  • 把每一个plugin存入plugins
  • 原生事件执行publishEventForPlugin;
function recomputePluginOrdering(){
  if (!eventPluginOrder) {
    // Wait until an `eventPluginOrder` is injected.
    return;
  }
  for(const pluginName in namesToPlugins) {
    const pluginModule = namesToPlugins[pluginName];
    const pluginIndex = eventPluginOrder.indexOf(pluginName);
    if(plugins[pluginIndex]){
      continue;
    }
    plugins[pluginIndex] = pluginModule;
    const publishedEvents = pluginModule.eventTypes;
    // eventName原生事件
    for(const eventName in publishedEvents){
      publishEventForPlugin(
        publishedEvents[eventName], 
        pluginModule,
        eventName,
      )
    }
  }
}

调用函数:publishEventForPlugin(publishedEvents[eventName], pluginModule, eventName)

  • 把所有plugin中的eventTypes中的eventTypes.eventName存入eventNameDispatchConfigs: {} eventNameDispatchConfigs = { change: {phasedRegistrationNames, dependencies}, click: {phasedRegistrationNames, dependencies}, ...more }
  • 对每一个合成事件执行publishRegistrationName, 注册每一个合成事件进入registrationNameModules, registrationNameDependencies
// pluginModule: 如:ChangeEventPlugin
// eventName: 原生事件
function publishEventForPlugin(
  dispatchConfig,
  pluginModule,
  eventName,
){

  eventNameDispatchConfigs[eventName] = dispatchConfig;

  const phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
  if (phasedRegistrationNames) {
    for (const phaseName in phasedRegistrationNames) {
      if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
        const phasedRegistrationName = phasedRegistrationNames[phaseName];
        publishRegistrationName(
          phasedRegistrationName,
          pluginModule,
          eventName,
        );
      }
    }
    return true;
  } else if (dispatchConfig.registrationName) {
    publishRegistrationName(
      dispatchConfig.registrationName,
      pluginModule,
      eventName,
    );
    return true;
  }
  return false;
}

function publishRegistrationName(
  registrationName,
  pluginModule,
  eventName,
) {
  registrationNameModules[registrationName] = pluginModule;
  registrationNameDependencies[registrationName] =
    pluginModule.eventTypes[eventName].dependencies;
}

四个导出对象: plugins、eventNameDispatchConfigs, registrationNameModules、registrationNameDependencies

通过injectEventPluginOrder、injectEventPluginsByName方法后求出了上述四个对象的值; 四个对象主要作用的是原生事件和React事件对应关系; 以及React事件和Plugin对应关系

注意: 在 ES6 module 中,基本类型变量都是引用关系,所以很容易修改变量值;

  • 调用injectEventPluginOrder, 设置eventPluginOrder,eventPluginOrder是DOMEventPluginOrder的一个copy;

  • 调用injectEventPluginsByName: 注册除ResponderEventPlugin的5个plugin;

把所有插件加入到namesToPlugins对象中,key对应的插件名称;

  • 调用recomputePluginOrdering

    把插件按顺序插入到plugins;并对每个插件中的eventTypes中的每个事件类型调用publishEventForPlugin;

  • 调用publishEventForPlugin

  • 设置eventNameDispatchConfigs对象,以事件名为key存储dispatchConfig,也就是上面插件中的eventTypes.change对应的对象

  • 对每一个phasedRegistrationNames里面的项执行publishRegistrationName,设置registrationNameModules,以事件名registrationName为存储模块,同时设置registrationNameDependencies,以事件名为registrationName存储事件的dependencies

注意:registrationNameModules在更新 DOM 节点的时候会被用到,registrationNameDependencies在绑定事件的使用会被用到。