当前位置:   article > 正文

React16源码: React中event事件系统初始化源码实现

React16源码: React中event事件系统初始化源码实现

event 事件系统初始化


1 )概述

  • react事件系统比较的复杂,它是基于dom的事件系统
  • 在dom事件系统上面进行了一个深度的封装
  • 它里面的很多实现逻辑都是自由的一套
  • 在初始化 react-dom 的源码的时候,会为react的事件系统注入 reactdom 相关的一些插件
  • 因为react事件系统,它有一个独立的模块,这个模块是一个公用性质的模块
  • 就是说它是可以给 react-dom 用,也可以给 react-native 用
  • 不同平台它们的事件系统可能会不一样,这个时候就对于不同的平台
  • 它们要去使用同一个 event 模块的时候,使用注入的方式来注入一些跟平台相关的逻辑在里面
  • 在这个模块,也是有一部分核心的内容是全平台通用的,这部分内容是react抽象出来的
  • 我们关注平台插件注入的一个流程,以及它插入之后到底做了什么事情
    • 首先要确定一个插件注入的顺序
      • 因为在react当中它的插件执行是会按照顺序来的
      • 如果不按顺序来,可能会出现一定的问题
    • 然后要注入插件模块
    • 最后要计算 registationNameModules 等属性
  • 在之前 completeWork 的时候,初始化 dom 节点的时候
    • 要去绑定 props 对应的 dom 的 attributes 的时候
    • 就有遇到过这个 registationNameModules 属性

2 )源码

定位到 packages/react-dom/src/client/ReactDOM.js#L20

import './ReactDOMClientInjection';
  • 1

再次定位到 packages/react-dom/src/client/ReactDOMClientInjection.js

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import * as EventPluginHub from 'events/EventPluginHub';
import * as EventPluginUtils from 'events/EventPluginUtils';

import {
  getFiberCurrentPropsFromNode,
  getInstanceFromNode,
  getNodeFromInstance,
} from './ReactDOMComponentTree';
import BeforeInputEventPlugin from '../events/BeforeInputEventPlugin';
import ChangeEventPlugin from '../events/ChangeEventPlugin';
import DOMEventPluginOrder from '../events/DOMEventPluginOrder';
import EnterLeaveEventPlugin from '../events/EnterLeaveEventPlugin';
import SelectEventPlugin from '../events/SelectEventPlugin';
import SimpleEventPlugin from '../events/SimpleEventPlugin';

/**
 * Inject modules for resolving DOM hierarchy and plugin ordering.
 */
EventPluginHub.injection.injectEventPluginOrder(DOMEventPluginOrder);
EventPluginUtils.setComponentTree(
  getFiberCurrentPropsFromNode,
  getInstanceFromNode,
  getNodeFromInstance,
);

/**
 * Some important event plugins included by default (without having to require
 * them).
 */
EventPluginHub.injection.injectEventPluginsByName({
  SimpleEventPlugin: SimpleEventPlugin,
  EnterLeaveEventPlugin: EnterLeaveEventPlugin,
  ChangeEventPlugin: ChangeEventPlugin,
  SelectEventPlugin: SelectEventPlugin,
  BeforeInputEventPlugin: BeforeInputEventPlugin,
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 看到它 import 了一大堆的东西,后续只是调用了3个方法

    • injectEventPluginOrder
    • setComponentTree 这个先跳过
    • injectEventPluginsByName
  • 看下 EventPluginHub.injection.injectEventPluginOrder(DOMEventPluginOrder);

    • 这个 DOMEventPluginOrder
      // packages/react-dom/src/events/DOMEventPluginOrder.js
      const DOMEventPluginOrder = [
        'ResponderEventPlugin',
        'SimpleEventPlugin',
        'EnterLeaveEventPlugin',
        'ChangeEventPlugin',
        'SelectEventPlugin',
        'BeforeInputEventPlugin',
      ];
      
      export default DOMEventPluginOrder;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 它单纯的 export 出来了一个数组
      • 这个数组可以看到有6项,每一项都以一个 Plugin 为结尾的
      • 这些 plugin 都是在 react-dom 这个环境当中要用到的 plugin
      • 这边只是用来定义这些 plugin 它的一个顺序
  • 后续 EventPluginHub.injection.injectEventPluginsByName 这个方法的参数

    • 发现这里少了一个 ResponderEventPlugin 先不管
  • 关注下 EventPluginHub 这个模块下的 injection

    /**
     * Methods for injecting dependencies.
     */
    export const injection = {
      /**
       * @param {array} InjectedEventPluginOrder
       * @public
       */
      injectEventPluginOrder,
    
      /**
       * @param {object} injectedNamesToPlugins Map from names to plugin modules.
       */
      injectEventPluginsByName,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 上述内部这两个方法来自 ./EventPluginRegistry.js 进入
      /**
       * Injects an ordering of plugins (by plugin name). This allows the ordering
      * to be decoupled from injection of the actual plugins so that ordering is
      * always deterministic regardless of packaging, on-the-fly injection, etc.
      *
      * @param {array} InjectedEventPluginOrder
      * @internal
      * @see {EventPluginHub.injection.injectEventPluginOrder}
      */
      export function injectEventPluginOrder(
        injectedEventPluginOrder: EventPluginOrder,
      ): void {
        invariant(
          !eventPluginOrder,
          'EventPluginRegistry: Cannot inject event plugin ordering more than ' +
            'once. You are likely trying to load more than one copy of React.',
        );
        // Clone the ordering so it cannot be dynamically mutated.
        // 克隆一个可动态修改的数组
        eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
        recomputePluginOrdering();
      }
      
      /**
       * Injects plugins to be used by `EventPluginHub`. The plugin names must be
      * in the ordering injected by `injectEventPluginOrder`.
      *
      * Plugins can be injected as part of page initialization or on-the-fly.
      *
      * @param {object} injectedNamesToPlugins Map from names to plugin modules.
      * @internal
      * @see {EventPluginHub.injection.injectEventPluginsByName}
      */
      export function injectEventPluginsByName(
        injectedNamesToPlugins: NamesToPlugins,
      ): void {
        let isOrderingDirty = false;
        // 遍历对象上的 pluginName
        for (const pluginName in injectedNamesToPlugins) {
          // 非本身拥有,则跳过
          if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
            continue;
          }
          const pluginModule = injectedNamesToPlugins[pluginName];
          if (
            !namesToPlugins.hasOwnProperty(pluginName) ||
            namesToPlugins[pluginName] !== pluginModule
          ) {
            invariant(
              !namesToPlugins[pluginName],
              'EventPluginRegistry: Cannot inject two different event plugins ' +
                'using the same name, `%s`.',
              pluginName,
            );
            // 重新注入 module
            namesToPlugins[pluginName] = pluginModule;
            isOrderingDirty = true; // 设置这个 isOrderingDirty 状态
          }
        }
        if (isOrderingDirty) {
          recomputePluginOrdering(); // 调用这个方法
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 上述 namesToPlugins 本来就是一个 空的对象
      • 进入 recomputePluginOrdering
        /**
         * Recomputes the plugin list using the injected plugins and plugin ordering.
        *
        * @private
        */
        function recomputePluginOrdering(): void {
          if (!eventPluginOrder) {
            // Wait until an `eventPluginOrder` is injected.
            return;
          }
          // 遍历在 injectEventPluginsByName 方法中处理好的 namesToPlugins 对象
          for (const pluginName in namesToPlugins) {
            const pluginModule = namesToPlugins[pluginName];
            const pluginIndex = eventPluginOrder.indexOf(pluginName); // 拿到注入顺序
            invariant(
              pluginIndex > -1,
              'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
                'the plugin ordering, `%s`.',
              pluginName,
            );
            // plugins 初始化的时候,是一个空的数组,存在则跳过
            if (plugins[pluginIndex]) {
              continue;
            }
            invariant(
              pluginModule.extractEvents,
              'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
                'method, but `%s` does not.',
              pluginName,
            );
            // 注意,这里的 index 是从 eventPluginOrder 的顺序插入的,而非有序插入,这里可能会造成数组中的某几项为 undefined
            plugins[pluginIndex] = pluginModule;
            const publishedEvents = pluginModule.eventTypes; // click, change, focus 等类型
            for (const eventName in publishedEvents) {
              invariant(
                // 注意这里
                publishEventForPlugin(
                  publishedEvents[eventName], // 注意这个数据结构
                  pluginModule,
                  eventName,
                ),
                'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
                eventName,
                pluginName,
              );
            }
          }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 关于 eventTypes
          const eventTypes = {
            // 这个 对应 dom 中的真实事件,比如 change 事件 document.addEventListener('change', () => {})
            // 这个 change 代表 event name 存在
            // 这个 value 对应上面的 dispatchConfig
            change: {
              // 事件的阶段,有冒泡和捕获 两个阶段,对应react中 使用的事件 props 名称
              phasedRegistrationNames: {
                bubbled: 'onChange',
                captured: 'onChangeCapture', // 这个 props 不常用,用于在绑定捕获阶段的事件监听
              },
              // 监听 change 事件的同时,需要依赖绑定下面的事件
              dependencies: [
                TOP_BLUR,
                TOP_CHANGE,
                TOP_CLICK,
                TOP_FOCUS,
                TOP_INPUT,
                TOP_KEY_DOWN,
                TOP_KEY_UP,
                TOP_SELECTION_CHANGE,
              ],
            },
          };
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • eventTypes 这个对象里面还可以再加其他事件,以上是初始化时候挂载处理的 change 事件,参考下面
        • 对于 packages/react-dom/src/events/SimpleEventPlugin.js 里面监听了大部分的常用事件
          • 在这里面 会生成一个 type, 定位到 #L143 (143行)
            function addEventTypeNameToConfig(
              [topEvent, event]: EventTuple,
              isInteractive: boolean,
            ) {
              const capitalizedEvent = event[0].toUpperCase() + event.slice(1);
              const onEvent = 'on' + capitalizedEvent;
            
              // 注意这里
              const type = {
                phasedRegistrationNames: {
                  bubbled: onEvent,
                  captured: onEvent + 'Capture',
                },
                dependencies: [topEvent],
                isInteractive,
              };
              eventTypes[event] = type;
              topLevelEventsToDispatchConfig[topEvent] = type;
            }
            
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
            • 19
        • 进入 publishEventForPlugin
          /**
           * Publishes an event so that it can be dispatched by the supplied plugin.
          *
          * @param {object} dispatchConfig Dispatch configuration for the event.
          * @param {object} PluginModule Plugin publishing the event.
          * @return {boolean} True if the event was successfully published.
          * @private
          */
          function publishEventForPlugin(
            dispatchConfig: DispatchConfig,
            pluginModule: PluginModule<AnyNativeEvent>,
            eventName: string,
          ): boolean {
            invariant(
              !eventNameDispatchConfigs.hasOwnProperty(eventName),
              'EventPluginHub: More than one plugin attempted to publish the same ' +
                'event name, `%s`.',
              eventName,
            );
            // 这里 eventNameDispatchConfigs 的结构
            // { change: ChangeEventPlugin.eventTypes.change }
            eventNameDispatchConfigs[eventName] = dispatchConfig;
          
            // 获取事件 内部的 phasedRegistrationNames
            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;
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 进入 publishRegistrationName
            /**
             * Publishes a registration name that is used to identify dispatched events.
            *
            * @param {string} registrationName Registration name to add.
            * @param {object} PluginModule Plugin publishing the event.
            * @private
            */
            function publishRegistrationName(
              registrationName: string,
              pluginModule: PluginModule<AnyNativeEvent>,
              eventName: string,
            ): void {
              invariant(
                !registrationNameModules[registrationName],
                'EventPluginHub: More than one plugin attempted to publish the same ' +
                  'registration name, `%s`.',
                registrationName,
              );
              // onChange: ChangeEventPlugin
              registrationNameModules[registrationName] = pluginModule;
              // onChange: [TOP_BLUR ...]
              registrationNameDependencies[registrationName] =
                pluginModule.eventTypes[eventName].dependencies;
            
              if (__DEV__) {
                const lowerCasedName = registrationName.toLowerCase();
                possibleRegistrationNames[lowerCasedName] = registrationName;
            
                if (registrationName === 'onDoubleClick') {
                  possibleRegistrationNames.ondblclick = registrationName;
                }
              }
            }
            
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
            • 19
            • 20
            • 21
            • 22
            • 23
            • 24
            • 25
            • 26
            • 27
            • 28
            • 29
            • 30
            • 31
            • 32
            • 33
        • 关于 const publishedEvents = pluginModule.eventTypes; 这里,可参考 packages/react-dom/src/events/ChangeEventPlugin.js#L258
          const ChangeEventPlugin = {
            eventTypes: eventTypes,
          
            _isInputEventSupported: isInputEventSupported, // 这个 _isInputEventSupported 是一个私有标志位
            
            // 这个 extractEvents 是生成事件,比如 onChange 事件对应的事件对象的
            extractEvents: function(
              topLevelType,
              targetInst,
              nativeEvent,
              nativeEventTarget,
            ) {
              const targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
          
              let getTargetInstFunc, handleEventFunc;
              if (shouldUseChangeEvent(targetNode)) {
                getTargetInstFunc = getTargetInstForChangeEvent;
              } else if (isTextInputElement(targetNode)) {
                if (isInputEventSupported) {
                  getTargetInstFunc = getTargetInstForInputOrChangeEvent;
                } else {
                  getTargetInstFunc = getTargetInstForInputEventPolyfill;
                  handleEventFunc = handleEventsForInputEventPolyfill;
                }
              } else if (shouldUseClickEvent(targetNode)) {
                getTargetInstFunc = getTargetInstForClickEvent;
              }
          
              if (getTargetInstFunc) {
                const inst = getTargetInstFunc(topLevelType, targetInst);
                if (inst) {
                  const event = createAndAccumulateChangeEvent(
                    inst,
                    nativeEvent,
                    nativeEventTarget,
                  );
                  return event;
                }
              }
          
              if (handleEventFunc) {
                handleEventFunc(topLevelType, targetNode, targetInst);
              }
          
              // When blurring, set the value attribute for number inputs
              if (topLevelType === TOP_BLUR) {
                handleControlledInputBlur(targetNode);
              }
            },
          };
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
          • 49
          • 50
  • 通过以上操作,插入了所有的plugin之后,形成了这边的几个变量

    • let eventPluginOrder: EventPluginOrder = null; 数据结构如下
      [
        'ResponderEventPlugin', 
        'SimpleEventPlugin', 
        'EnterLeaveEventPlugin', 
        'ChangeEventPlugin', 
        'SelectEventPlugin', 
        'BeforeInputEventPlugin'
      ];
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • export const plugins = []; 数据结构如下
      [
          {
              eventTypes:{},
              extractEvents:function,
              otherProps
          },
          ....
      ]
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • export const eventNameDispatchConfigs = {};
      {
          click:{
              dependencies:['click'],
              phasedRegistrationNames:{
                  bubbled: "onClick"
                  captured: "onClickCapture"
              },
              isInteractive: true
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • const namesToPlugins: NamesToPlugins = {};
      {
          SimpleEventPlugin:{
              eventTypes:{},
              extractEvents:function,
              otherProps
          },
          // ...其他插件
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • export const registrationNameModules = {};
      {
          onClick:{
              eventTypes:{},
              extractEvents:function,
              otherProps
          },
          ...
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • export const registrationNameDependencies = {};
      {
          onClick: ["click"],
          onChange: [
              "blur", "change", "click", "focus", "input", "keydown",keyup", "selectionchange
              ],
          ....
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
  • 把这几个变量维护好之后,后面可以很方便的进行一些事件绑定相关的操作

  • 对于事件注入这个模块,是初始化事件的前置任务

  • 重点关注最终拿到的几个完成注册之后的变量的数据格式

  • 以上就是把整个事件的插件它注入到react事件系统当中的过程

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号