React源码浅入(二)

前言

本文着重分析在React源码浅入(一)中提到的两个函数:ReactElement.createElement 和 ReactElement.cloneElement,从上面两个方法,我们可以看到他们都是出自与ReactElement这个对象。那么,先大体看下ReactElement这个对象里有什么:

  • createElement
  • createFactory
  • cloneAndReplaceKey
  • cloneElement

回顾下之前的调用方式:

ReactElement.createElement.apply(this, arguments)复制代码

那我们先来看下,这个arguments是何方神圣,console打出来之后:

demo1: createElement arguments

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) {
   // 根据config,设置ref、key、self、source。 config相当于props。
    ...
  }

  // Children can be more than one argument,and those are 
  // transferred onto the newly allocated props object.
  // 目的:将children赋值给props.

  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;
  }

  // 从上述代码来看,children.length === 1的时候,props.children是个object,
  // children.length > 1的时候,是个数组。当参数children没传的时候,props.children是undefined,所以说阮一峰老师说的这句话:
  // React 提供一个工具方法React.Children.map来处理 this.props.children 。
  // 我们可以用React.Children.map 来遍历子节点,而不用担心 this.props.children 
  // 的数据类型是 undefined 还是 object

  // 分配默认属性,不包含保留字段,比如ref、key等。
  if (type && type.defaultProps) {
    var defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  // 设置key、ref的configurable,表示可delete该属性,可重置该属性。详细关于描述符的解释,
  // 可参照https://segmentfault.com/a/1190000007434923

  if ("development" !== 'production') {
    // 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 () {
            return undefined;
          },
          configurable: true
        });
      }
      if (!props.hasOwnProperty('ref')) {
        Object.defineProperty(props, 'ref', {
          get: function () {
            return undefined;
          },
          configurable: true
        });
      }
    }
  }
  return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
};复制代码

ReactCurrentOwner 所有代码如下:

'use strict';

/**
 * Keeps track of the current owner.
 *
 * The current owner is the component who should own any components that are
 * currently being constructed.
 */

var ReactCurrentOwner = {

  /**
   * @internal
   * @type {ReactComponent}
   */
  current: null

};复制代码

createElement方法最终调用的是ReactElement。我们再来看看ReactElement这个函数。首先看下这个函数上面的注释:

/**
 * Factory method to create a new React element. This no longer adheres to
 * the class pattern, so do not use new to call it. Also, no instanceof check
 * will work. Instead test $$typeof field against Symbol.for('react.element') to check
 * if something is a React Element.
 */复制代码

大体翻译是工厂模式,创建新的React元素。这里不在运用类模式创建,所以不需要new一个实例出来,因此,也没有instance of方法。取而代之,用 $$typeof === == REACT_ELEMENT_TYPE ( REACT_ELEMENT_TYPE=Symbol.for('react.element'))来判断是否是react元素。

获取到的关键信息如下:

  • Factory method工厂模式,不需要new实例。故没有 instanceof 方法
  • $$typeof === == REACT_ELEMENT_TYPE来判断是否是react元素

最后返回的element如下(一个稍微复杂的):

ReactElement 函数最终返回结果

好了,这就是传说中的Virtual Dom,React中的React Node了。从途中,能看到_owner这个属性,是个ReactCompositeComponentWrapper 这个东西!!!!这是个什么东西,咱们后续再说。

好了,总结一下createElement的目的,将一组属性,组成一个对象。

#ReactElement.cloneElement
ReactElement.cloneElement 和ReactElement.createElement 大同小异,赋值属性,调用ReactElement函数。不同的一点,只是,属性的与config转入的合并,且config的优先级 > clone的element

ReactElement.cloneElement = function (element, config, children) {
  var propName;

  // Original props are copied
  var props = _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);
};复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值