React组件的初始化过程


  • administrators

    组件的初始化

    ES6的"类"是如何初始化为一个react组件的?
    0_1520959335332_react_componet_1.png
    《图一》
    0_1520959350333_react_component_2.png
    《图二》
    我们发现render方法实际上是调用了React.createElement方法(实际是ReactElement方法)
    React.Component --> React.createElement --> ReactElement

    React.Component源码分析!

    function Component(props, context, updater) {
      this.props = props;
      this.context = context;
      this.refs = emptyObject;
      // We initialize the default updater but the real one gets injected by the
      // renderer.
      this.updater = updater || ReactNoopUpdateQueue;
    }
    Component.prototype.setState = function (partialState, callback) {
      !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0;
      this.updater.enqueueSetState(this, partialState, callback, 'setState');
    };
    Component.prototype.forceUpdate = function (callback) {
      this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
    };
    

    上述代码Component的构造函数,想必大家已经记得住了。同时我们也注意到setState是定义在原型上具有两个参数的方法,具体原理我们将在React更新机制的课程中讲解。
    上述代码表明,我们在最开始声明的组件App,其实是继承React.Component类的子类,它的原型具有setState等方法。这样组件App已经有了最基本的雏形。

    React.createElement源码分析!

    function createElement(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 (hasValidRef(config)) {
          ref = config.ref;
        }
        if (hasValidKey(config)) {
          key = '' + config.key;
        }
        //todo self 和 source 都是通过 props 传入的
        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) {
            //todo 检验是不是 OwnProperty,且不能是保留的名称。属性名不能是 key, ref, __self 和 __source
            if (hasOwnProperty.call(config, 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.
      //todo createElement 可以接受不止三个参数,在 children 后面传入更多的参数,表示有更多的 child,因此这里要把它们收集起来。
      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];
        }
        {
          if (Object.freeze) {
            Object.freeze(childArray);
          }
        }
        props.children = childArray;
      }
    
      //todo 把组件的默认属性值赋予组件的实例上
      // Resolve default props
      if (type && type.defaultProps) {
        var defaultProps = type.defaultProps;
        for (propName in defaultProps) {
          if (props[propName] === undefined) {
            props[propName] = defaultProps[propName];
          }
        }
      }
      {
        if (key || ref) {
          if (typeof props.$$typeof === 'undefined' || props.$$typeof !== REACT_ELEMENT_TYPE) {
            var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
            if (key) {
              defineKeyPropWarningGetter(props, displayName);
            }
            if (ref) {
              defineRefPropWarningGetter(props, displayName);
            }
          }
        }
      }
      return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
    }
    

    ReactElement 源码分析!

    ReactElement 是一个函数,接受比较多的参数。

    • $$typeof:组件的标识信息

    • type: 使用 React.createElement创建的 React 组件

    • key: 使用 React 组件创建的 ReactElement 的 key,DOM结构标识,提升update性能

    • ref: 这个 ReactElement 的 ref,真实DOM的引用
      其他几个在开发环境下起作用,便于调试。

    • props:子结构相关信息(有则增加children字段/没有为空)和组件属性(如style)

    • self: 用来记录当前元素所在的环境,因为创建组件的时候是一个递归的过程,对于每一层的组件都有其自己的 this 值。

    • source: 中包含了一些文件名称,行号等信息。

    • owner: 用来记录该元素所属的组件。值为创建当前组件的对象,默认值为null。
      ReactElement 这个函数只是将以上参数放入一个对象,并返回。

    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
      };
    
      {
        // 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.
        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
        });
        if (Object.freeze) {
          Object.freeze(element.props);
          Object.freeze(element);
        }
      }
    
      return element;
    };
    

    看完上述内容相信大家已经对React组件的实质有了一定的了解。通过执行React.createElement创建出的ReactElement类型的js对象,就是"React组件",这与控制台打印出的结果完全对应。总结来说,如果我们通过class关键字声明React组件,那么他们在解析成真实DOM之前一直是ReactElement类型的js对象。

    总结

    ReactElement 和 React.Component 的关系
    ReactElement 的 type 属性是 React.Component,仅此而已。


  • React高级班

    PureComponent和Component


Log in to reply
 

Looks like your connection to 新前端社区 was lost, please wait while we try to reconnect.