zoukankan      html  css  js  c++  java
  • React源码解析:ReactElement

    ReactElement算是React源码中比较简单的部分了,直接看源码:

    var ReactElement = function(type, key, ref, self, source, owner, props) {
      var element = {
        // This tag allow us to uniquely identify this as a React Element
        $$typeof: REACT_ELEMENT_TYPE,
    
        // Built-in properties that belong on the element
        type: type,
        key: key,
        ref: ref,
        props: props,
    
        // Record the component responsible for creating this element.
        _owner: owner,
      };
    
      if (__DEV__) {
        // The validation flag is currently mutative. We put it on
        // an external backing store so that we can freeze the whole object.
        // This can be replaced with a WeakMap once they are implemented in
        // commonly used development environments.
        element._store = {};
    
        // To make comparing ReactElements easier for testing purposes, we make
        // the validation flag non-enumerable (where possible, which should
        // include every environment we run tests in), so the test framework
        // ignores it.
        if (canDefineProperty) {
          Object.defineProperty(element._store, 'validated', {
            configurable: false,
            enumerable: false,
            writable: true,
            value: false,
          });
          // self and source are DEV only properties.
          Object.defineProperty(element, '_self', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: self,
          });
          // Two elements created in two different places should be considered
          // equal for testing purposes and therefore we hide it from enumeration.
          Object.defineProperty(element, '_source', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: source,
          });
        } else {
          element._store.validated = false;
          element._self = self;
          element._source = source;
        }
        if (Object.freeze) {
          Object.freeze(element.props);
          Object.freeze(element);
        }
      }
    
      return element;
    };

    可以看出ReactElement就是一个函数,传入一系列参数作为一个element对象的属性,然后再把这个对象return出来,但是注意到有一个属性是$$typeof: REACT_ELEMENT_TYPE,然后我查找了一下这个REACT_ELEMENT_TYPE的定义:

    // The Symbol used to tag the ReactElement type. If there is no native Symbol
    // nor polyfill, then a plain number is used for performance.
    var REACT_ELEMENT_TYPE =
      (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
      0xeac7;

    根据注释可以看出这个属性是用来标记当前对象是一个ReactElement,如果不是原生Symbol类型或者填充,则使用普通数字来代替,这也区分了element并非普通对象,而是特殊的ReactElement。

    接下来我们再来看一下ReactElement.createElement这个方法,还是直接来看源码:

    ReactElement.createElement = function(type, config, children) {
      var propName;
    
      // Reserved names are extracted
      var props = {};
    
      var key = null;
      var ref = null;
      var self = null;
      var source = null;
    
      if (config != null) {
        if (__DEV__) {
          ref = !config.hasOwnProperty('ref') ||
            Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref;
          key = !config.hasOwnProperty('key') ||
            Object.getOwnPropertyDescriptor(config, 'key').get ? null : '' + config.key;
        } else {
          ref = config.ref === undefined ? null : config.ref;
          key = config.key === undefined ? null : '' + config.key;
        }
        self = config.__self === undefined ? null : config.__self;
        source = config.__source === undefined ? null : config.__source;
        // Remaining properties are added to a new props object
        for (propName in config) {
          if (config.hasOwnProperty(propName) &&
              !RESERVED_PROPS.hasOwnProperty(propName)) {
            props[propName] = config[propName];
          }
        }
      }
    
      // Children can be more than one argument, and those are transferred onto
      // the newly allocated props object.
      var childrenLength = arguments.length - 2;
      if (childrenLength === 1) {
        props.children = children;
      } else if (childrenLength > 1) {
        var childArray = Array(childrenLength);
        for (var i = 0; i < childrenLength; i++) {
          childArray[i] = arguments[i + 2];
        }
        props.children = childArray;
      }
    
      // Resolve default props
      if (type && type.defaultProps) {
        var defaultProps = type.defaultProps;
        for (propName in defaultProps) {
          if (props[propName] === undefined) {
            props[propName] = defaultProps[propName];
          }
        }
      }
      if (__DEV__) {
        // Create dummy `key` and `ref` property to `props` to warn users
        // against its use
        if (typeof props.$$typeof === 'undefined' ||
            props.$$typeof !== REACT_ELEMENT_TYPE) {
          if (!props.hasOwnProperty('key')) {
            Object.defineProperty(props, 'key', {
              get: function() {
                if (!specialPropKeyWarningShown) {
                  specialPropKeyWarningShown = true;
                  warning(
                    false,
                    '%s: `key` is not a prop. Trying to access it will result ' +
                      'in `undefined` being returned. If you need to access the same ' +
                      'value within the child component, you should pass it as a different ' +
                      'prop. (https://fb.me/react-special-props)',
                    typeof type === 'function' && 'displayName' in type ? type.displayName : 'Element'
                  );
                }
                return undefined;
              },
              configurable: true,
            });
          }
          if (!props.hasOwnProperty('ref')) {
            Object.defineProperty(props, 'ref', {
              get: function() {
                if (!specialPropRefWarningShown) {
                  specialPropRefWarningShown = true;
                  warning(
                    false,
                    '%s: `ref` is not a prop. Trying to access it will result ' +
                      'in `undefined` being returned. If you need to access the same ' +
                      'value within the child component, you should pass it as a different ' +
                      'prop. (https://fb.me/react-special-props)',
                    typeof type === 'function' && 'displayName' in type ? type.displayName : 'Element'
                  );
                }
                return undefined;
              },
              configurable: true,
            });
          }
        }
      }
      return ReactElement(
        type,
        key,
        ref,
        self,
        source,
        ReactCurrentOwner.current,
        props
      );
    };

    有点长,但其实也很简单,总结一下就是四点:

    1,如果有config参数,那么就把config内的同名ReactElement属性给替换掉;

    2,如果arguments.length为3,表示有第三个参数children,并且children只有一个,那么直接进行赋值同名属性children,如果大于3,表示children不止一个,就直接for循环赋值;

    3,如果存在type参数并且有defaultprops静态变量,那么就循环defaultProps的属性,只要属性名不为undefined,就赋值给props同名属性;

    4,不允许向props添加key和ref属性;

    最后返回创建的ReactElement。

    还有一个常用的是React.cloneElement:

    ReactElement.cloneElement = function(element, config, children) {
      var propName;
    
      // Original props are copied
      var props = Object.assign({}, element.props);
    
      // Reserved names are extracted
      var key = element.key;
      var ref = element.ref;
      // Self is preserved since the owner is preserved.
      var self = element._self;
      // Source is preserved since cloneElement is unlikely to be targeted by a
      // transpiler, and the original source is probably a better indicator of the
      // true owner.
      var source = element._source;
    
      // Owner will be preserved, unless ref is overridden
      var owner = element._owner;
    
      if (config != null) {
        if (config.ref !== undefined) {
          // Silently steal the ref from the parent.
          ref = config.ref;
          owner = ReactCurrentOwner.current;
        }
        if (config.key !== undefined) {
          key = '' + config.key;
        }
        // Remaining properties override existing props
        var defaultProps;
        if (element.type && element.type.defaultProps) {
          defaultProps = element.type.defaultProps;
        }
        for (propName in config) {
          if (config.hasOwnProperty(propName) &&
              !RESERVED_PROPS.hasOwnProperty(propName)) {
            if (config[propName] === undefined && defaultProps !== undefined) {
              // Resolve default props
              props[propName] = defaultProps[propName];
            } else {
              props[propName] = config[propName];
            }
          }
        }
      }
    
      // Children can be more than one argument, and those are transferred onto
      // the newly allocated props object.
      var childrenLength = arguments.length - 2;
      if (childrenLength === 1) {
        props.children = children;
      } else if (childrenLength > 1) {
        var childArray = Array(childrenLength);
        for (var i = 0; i < childrenLength; i++) {
          childArray[i] = arguments[i + 2];
        }
        props.children = childArray;
      }
    
      return ReactElement(
        element.type,
        key,
        ref,
        self,
        source,
        owner,
        props
      );
    };

     如果config里面有ref,key,props,则全部替换掉config里面的,同名的props直接覆盖,同时参数的children,也作为新的children,最后返回赋值后的ReactElement;

    最后还有一个检测ReactElement是否为有效的方法ReactElement.isValidElement:

    /**
     * @param {?object} object
     * @return {boolean} True if `object` is a valid component.
     * @final
     */
    ReactElement.isValidElement = function(object) {
      return (
        typeof object === 'object' &&
        object !== null &&
        object.$$typeof === REACT_ELEMENT_TYPE
      );
    };

    而这个REACT_ELEMENT_TYPE前面已经介绍过了,这里不再多说。

    1

  • 相关阅读:
    MyEclipse添加SVN插件
    Postgresql的character varying = bytea问题
    Hibernate主键增加方式
    java配置环境变量
    Maven常用构建命令
    Postgresql的主键自增长
    js判断金额
    最精简的SQL教程
    SQL练习1:统计班级男女生人数
    sql 百万级数据库优化方案
  • 原文地址:https://www.cnblogs.com/yanchenyu/p/8304981.html
Copyright © 2011-2022 走看看