React源码学习(1)------React.createElement

170 阅读2分钟

什么是React.createElement?

React.createElement 是React提供的一个高阶函数,用于根据传入的参数动态地创建React元素。这个函数接收三个及以上的参数:元素类型(type)、属性(props)、以及子元素(children)。

React.createElement的工作原理

当你调用React.createElement时,它实际上会创建一个轻量级的对象,这个对象描述了你想在UI中渲染的React元素。这个对象包含了typepropskey(如果提供了的话)、ref(如果提供了的话)等关键信息。React后续会使用这些信息来构建和更新DOM。

const element = React.createElement(  
  'div',  
  { id: 'myDiv', className: 'myClass' },  
  ['Hello, React!', React.createElement('span', null, 'This is a span.')]
);  
  
// element 对象大致结构  
{  
  $$typeof: Symbol.for('react.element'),  
  type: 'div',  
  props: {  
    id: 'myDiv',  
    className: 'myClass',  
    children: ['Hello, React!', {/* span 元素的描述对象 */}]  
  },  
  key: null,  
  ref: null,  
  _owner: null,  
  _store: {}  
}

源码

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

  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  if (config != null) {
    // 将 config 处理后赋值给 props
    // ...省略
  }

  const childrenLength = arguments.length - 2;
  // 处理 children,会被赋值给props.children
  // ...省略

  // 处理 defaultProps
  // ...省略

  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // 标记这是个 React Element
    $$typeof: REACT_ELEMENT_TYPE,

    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
  };

  return element;
};

我们可以看到,这段代码将输入的三个参数打包成ReactElement返回,而ReactElement这个函数的返回值是一个React元素对象。也就是说,JSX的执行结果是一个ReactElement对象。

JSX与React.createElement的关系

实际上,在React中,我们通常使用JSX语法来定义React Element。然而,在浏览器实际运行之前,即在构建过程中,JSX会被Babel等编译器转换为React.createElement函数调用,这些调用会生成React Element对象。

比如我定义了这样一段jsx代码

<div key='1' classname='1'>
  <span>11</span>
</div>

经过babel编译以后的结果

image.png

可以看到这个jsx的执行结果转换成了React.createElement函数调用

同样的,我们在React中打印出一个React Element对象

const jsx = <div key='1' classname='1'>
  <span>11</span>
</div>
console.log(jsx);

image.png

实现React.createElement方法

function createElement(type, config, children) {
  const props = {}
  // 处理 config 对象
  if (config) {
    for (let propName in config) {
      if (propName !== 'key' && propName !== 'ref') {
        props[propName] = config[propName]
      }
    }
  }
  // 处理 children 
  if (children?.length) {
    props.children = children.length > 1 ? children : children[0]
  }
  // 添加 key 和 ref 到 props中
  if (config?.key) {
    props.key = config.key
  }
  if (config?.ref) {
    props.ref = config.ref
  }
  // 返回一个对象,描述了这个React元素
  return {
    $$$typeof: Symbol.for('react.element'), // 一个用于标记这是React元素的Symbol
    type,
    props,
    key: props?.key || null,
    ref: props?.ref || null
  }
}

控制台测试

const children = [createElement('span', null, '11'), createElement('span', null, '22')]
const element = createElement('div', { key: '1', classname: '1' }, children)
console.log(element);

image.png