三羊

三羊的小站

阅读react源码--ReactElement部分

May 30, 2017/「 react / Edit on Github ✏️

ReactElement 对象为 React 提供了createElementcreateFactorycloneElementisValidElement四个方法。ReactElement 是一个工厂方法,不是类模式,不要使用new去调用。检查一个对象是否是 react element 对象,通过检查这个对象的$$typeof是否等于Symbol.for('react.element')

var ReactElement=function(type,key,ref,self,source,owner,props){
  var element={
	$$typeof:REACT_ELEMENT_TYPE,// Symbol['for']('react.element')
    type:type,
    key:key,
    ref:ref,
    props:props,
    _owner:owner
  };
  if(process.env.NODE_ENV!=='production'{
      // ....... 增加一些其他的属性
      if(Object.freeze){
      Object.freeze(element.props);
      Object.freeze(element);
    }
  })
  return element;
}
  • createElement,第一个参数是type,实际是一个 Component 的构造函数,在type.defaultProps上定义默认的props值;第二个参数config是一个对象或者null,对象提供了props名,也可以提供keyref__self__source,第三个参数及后续的参数是children
ReactElement.createElement=function(type,config,children){
  var propName;
  var props={};
  var key=null,ref=null,self=null,source=null;
  if(config!=null){
    if(hasValidRef(config)){
      ref=config.ref;
    }
    if(hasValidKey(config)){
      key=''+config.key;
    }
  }
  self=config.__self===undefined?null:config.__self;
  source=config.__source===undefined?null:config.__source;
  // 赋值config里配置的props
  for(propName in config){
    if(hasOWnProperty.call(config,propName) && !['key','ref','__selft','__source'].includes(propName)){
      props[propName]=config[propName];
    }
  }
  // 赋值props.children
  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;
  }
  // 赋值props的默认值
  if(type && type.defaultProps){
    var defaultProps=type.defaultProps;
    for(propName in defaultProps){
      if(props[propName]===undefined){
        props[propName]=defaultProps[propName];
      }
    }
  }
  // .... 非开发环境配置
  // 返回react elment
  return ReactElement(type,key,ref,self,source,ReactCurrentOwner.current,props);
}
  • createFactory,这个方法返回一个创建 react element 的函数。接受一个参数,type,用于指定要创建的 element 的类型。
ReactElement.createFactory = function(type) {
  var factory = ReactElemet.createElement.bind(null, type)
  factory.type = type
  return factory
}
  • cloneElement,这个方法用于克隆一个已存在的 element,第一个参数就是element,用于被复制的,第二个是config,第三个是children,第二个和第三个参数与createElement意义一样,用于设置 props 和 children 的。其中,如果指定了 config,则会覆盖原 element 中的 props,如果指定了 children,则同样会覆盖原 element 的 children。
ReactElement.cloneElement = function(element, config, children) {
  var propName
  // 先复制原element的props和key,ref.
  var props = _assign({}, element.props)
  var key = element.key,
    ref = element.ref
  var self = element._self,
    source = element._source,
    owner = element._owner
  // 如果config不为null,则覆盖原element的props和,key,self..
  if (config != null) {
    if (hasValidRef(config)) {
      ref = config.ref
      owner = ReactCurrentOwner.cuurent
    }
    if (hasValidKey(config)) {
      key = config.key
    }
    var defaultProps
    if (element.type && element.defaultProps) {
      defaultProps = element.type.defaultProps
    }
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !["key", "ref", "__self", "__source"].includes(propName)
      ) {
        if (
          config[propName] === undefined &&
          defaultProps[propName] !== undefined
        ) {
          props[propName] = defaultProps[propName]
        } else {
          props[propName] = config[propName]
        }
      }
    }
  }
  // 如果children不为null,则重新指定新的element的children
  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
  }

  // 最后也是使用ReactElement工厂创建新的react element
  return ReactElement(element.type, key, ref, self, source, owner, props)
}
  • isValidElement,这个方法是用于来判断一个对象是否是 react element 对象。判断的依据就是 react element 的对象会有一个属性$$typeof,它等于Symbol['for']('react.element')
ReactElement.isValidElement = function(object) {
  return (
    typeof object === "object" &&
    object !== null &&
    object.$$typeof === Symbol["for"]("react.element")
  )
}

「react 版本 15.5.4」

若有收获,小额鼓励