In layman's language React core source code parsing (2) createElement and ReactElement

一、createElement

The last chapter we talked about all jsx grammar will be converted into createElement.

Then the realization of createElement what is it?

First, we cloned from github react down the source library, we first analyze the file layout under react source library.

There react under the project root folder packages, is placed between each packet react, we have to put in the focus react directory. The interior is react source code implementation.

Some non-essential thrown to detect, and warn the code, the core react only a few hundred lines of code in fact. react source itself is not complicated, responsible for rendering the react-dom is the most complex.

src react directory, is the core react achieved.

createElement methods are in ReactElement.js file, implemented as follows:


export function createElement(type, config, children) {
  let propName;

  // Reserved names are extracted
  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let 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;
    // Remaining properties are added to a new props object
    for (propName in config) {
      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.
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    if (__DEV__) {
      if (Object.freeze) {
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  }

  // Resolve default props
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  if (__DEV__) {
    if (key || ref) {
      const 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,
  );
}


复制代码

And there are some tests under development environment, and external calls methods may make the reader distracted, let down a little change to streamline the code to make the same function, and better reading:

export function createElement(type, config, ...children) {
  const {ref = null, key = null} = config || {};
  const {current} = ReactCurrentOwner;
  const {defaultProps} = type || {};
  const props = assignProps(config, defaultProps, children);

  return new ReactElement({
    type,
    key: '' + key,
    ref,
    current,
    props,
  });
}
复制代码

After streamline and simplify, createElement only 30 lines of code. We resolved the next line by line.

/**
 * 
 * @param type {string | function | object}  
 *        如果type是字符串,那就是原生dom元素,比如div
 *        如果是function或者是Component的子类 则是React组件
 *        object 会是一些特殊的type 比如fragment
 * @param config {object}
 *        props 和key 还有ref 其实都是在config里了
 * @param children
 *        就是由其他嵌套createElement方法返回的ReactElement实例
 * @returns {ReactElement}
 * 
 */
export function createElement(type, config, ...children) {
    
  // 给config设置一个空对象的默认值
  // ref和key 默认为null
  const {ref = null, key = null} = config || {};
  // ReactCurrentOwner负责管理当前渲染的组件和节点
  const {current} = ReactCurrentOwner;
  // 如果是函数组件和类组件 是可以有defaultProps的
  // 比如
  // function A({age}) {return <div>{age}</div>}
  // A.defaultProps = { age:123 }
  const {defaultProps} = type || {};
  // 把defaultProps和props 合并一下
  const props = assignProps(config, defaultProps, children);
  // 返回了一个ReactElement实例
  return new ReactElement({
    type,
    key: '' + key,
    ref,
    current,
    props,
  });
}

复制代码

ref and key Needless to say, we all know is Gansha. Before a colleague asked me, obviously key transmission is digital, why had become a string, just above the crux of ReactELement constructor parameter passing key there, key:''+key.

assignProps I abstract a way to merge defaultProps incoming props and methods, providing the code later, in fact, cloneElement method, there are some similar code, but did not react abstracted, relatively speaking, there will be code redundancy, for the time being extracted.

Focus on new ReactElement ().

react the code, ReactElement a factory function that returns an object. But I personally feel rather strange.

First, factory function instance, the factory should begin with a capital function.

Second, use the constructor or class to declare ReactElement Is not it a better, more consistent selection semantics?

Here, for ease of understanding, the examples of functions ReactElement plant, has varied from a class, is a return to the createElement ReactElement class.

Here look achieve asssignProps, the method may be multiplexed in cloneElement:


const RESERVED_PROPS = ['key', 'ref', '__self', '__source'];

export function assignProps(config, defaultProps, children) {
  
    const props = {
        children,
    };
    config = config || {};
    for (const propName in config) {
        if (
            config.hasOwnProperty(propName) &&
            !RESERVED_PROPS.includes(propName)
        ) {
            props[propName] = config[propName];
            if (
                props[propName] === undefined &&
                defaultProps &&
                defaultProps[propName] !== undefined
            ) {
                props[propName] = defaultProps[propName];
            }
        }
    }

    return props;
}


复制代码

二、ReactElement

create returns a ReactElement instance, then ReactElement is Shane?

Code when thrown to the dev, after streamlined as follows:


const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE,
    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
  };

  return element;
};


复制代码

You can see, in fact, returns an object, we are now under a simple and grandiose imagination, render mechanisms react is actually read these data structures, and the structure of the tree, rendering layers made according to the native dom methods. (Temporarily so imagine)

After the code transformation of a class:


export class ReactElement {
  constructor(elementParams) {
    const {type, key, ref, current, props} = elementParams || {};
    // 如果是原生标签比如h1 那就是字符串
    // 如果是组件 则是组件的引用
    this.type = type;
    // key
    this.key = key;
    // ref
    this.ref = ref;
    // 延后再讲
    this._owner = current;
    // props
    this.props = props;
    // 类型标识 新版本中的React里是symbo
    this.$$typeof = REACT_ELEMENT_TYPE;
  }
}

复制代码

Third, the summary

Chapter is focused, react in, the nature of the label is jsx ReactElement, createElement dom will components or through the layer of encapsulation type, and props, and finally returns the ReactElement example.

Guess you like

Origin blog.csdn.net/weixin_33966095/article/details/91375208