头图

What is JSX?

Official definition: JSX is React.createElement(components, props, ...children) Syntax sugar for functions

 <Mybutton color="blue" shadowSize={2}>
Click Me
</Mybutton>

The above function will be compiled to the following code:

 React.createElement(
  Mybutton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

Open the source code of React and find the createElement function:

 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;

      {
        warnIfStringRefCannotBeAutoConverted(config);
      }
    }

    if (hasValidKey(config)) {
      {
        checkKeyStringCoercion(config.key);
      }

      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.

  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;
  } // 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) {
      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);
}

From the source code, we can see createElement function receives three parameters: type (label type), config (attribute in label), children (sublabel)

However, when writing JSX , a tag usually contains multiple subtags, so how does the function receive these subtags?

We focus on this part of the source code:

 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;
  } // Resolve default props

We know that the arguments object contains all parameters, then childrenLength is the number of remaining parameters excluding the first two parameters

If childrenLength is 1, there is only one child element (can be text or new JSX) If childrenLength is greater than 1, create an array of length childrenLength, and use a for loop to add the objects in arguments to the array

A simplified version of React.createElement

ReactElement Object definition:

 function ReactElement(type, key, props) {
    return {
        $$typeof: Symbol.for('react.element'),
    type,
    key,
    props
    }
}

createElement() Function implementation:

 function createElement(type, config, children) {
    const props = {};
  if (config) {
        // 将 config 中的键值对添加到 props 中
    for (propName in config) {
      if (hasOwnProperty.call(config, propName)) {
        props[propName] = config[propName];
      }
    }
  }

  const childrenLength = arguments.length - 2;
  // 多个children使用数组的形式
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[2 + i];
    }
    props.children = childArray;
  }
  
  return ReactElement(type, null, props);
}

When there is only one DOM node, call the createElement function:

 let a = createElement(
  'div',
  {width:'20px', height: '20px'},
)

prints out a, which results in:

 {
  '$$typeof': Symbol(react.element),
  type: 'div',
  key: null,
  props: { width: '20px', height: '20px' }
}

Since there are no child elements, there is no children attribute in props

When the parent node has multiple child nodes:

 let a = createElement(
  'div',
  {width:'20px', height: '20px'},
  createElement(
    'p'
  ),
  createElement(
    'a'
  ),
)

prints out a, which results in:

 {
  '$$typeof': Symbol(react.element),
  type: 'div',
  key: null,
  props: { width: '20px', height: '20px', children: [ [Object], [Object] ] }
}

As you can see, when there are multiple child elements, children store these child elements in the form of an array.


Lyx
1 声望368 粉丝