createElement源码学习

183 阅读2分钟

createElement (1).png

前言

通过学习冴羽老师的react源码文章,写一篇自己理解的读后感。
文章会通过一些问答的形式,帮助大家更快的记住一些知识点。

什么是babel

react中,采用jsx语法,通过babel进行转换,转换成浏览器可以执行的js代码。
babel是一个javascript的编译器,可以将JSX编译成javascript函数

babel将jsx转换成什么

babel最终会将jsx代码,转换成React.createElement()函数。

<div id="foo">bar</div>
// babel编译后
React.createElement("div", {id: "foo"}, "bar");

createElement()函数参数

createElement(type, config, children)接受三个参数
第一个参数是元素类型,第二个参数是元素属性,第三个参数是子元素。

createElement源码解析

学习前可先下载React源码,和这篇文章一起学习。 git仓库地址:github.com/facebook/re…

  1. 查看packages/react/index.js文件
export {createElement} from './src/React'
  1. 进入./src/React文件
import {createElementWithValidation} from './ReactElementValidator'
import {createElement as createElementProd} from './ReactElement';
const createElement = __DEV__ ? createElementWithValidation : createElementProd;
export {createElement}
  1. 进入ReactElement文件 (重点)
const RESERVED_PROPS = {
    key: true,
    ref: true,
    __self: true,
    __source: true, 
};

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

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

  // 第一步逻辑:将一些判断条件省略,更方便阅读源码
  let key = '' + config.key;
  let ref = config.ref; 
  let self = config.__self; 
  let source = config.__source;

  
  // 第二步逻辑:构建props对象,去除传入的key、ref、__self、__source
  for (propName in config) {
    if (
      hasOwnProperty.call(config, propName) &&
      !RESERVED_PROPS.hasOwnProperty(propName)
    ) {
      props[propName] = config[propName];
    }
  }

 // 第三步逻辑:将children参数进行处理。children可能有多个参数
 // 当children只有一个元素时,它是一个Object。当children有多个子元素时,它是是一个对象数组
  props.children = children;
 

  // 第四步逻辑:对type处理,处理组件的defaultProps
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }

 // 第五步 将处理好的数据传入ReactElement函数
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}
  • createElement作用?
    createElement主要做一个预处理的作用,处理传入的参数,将处理好的数据传入ReactElement函数
  • 为什么通过props获取不到key,ref
    在ceateElement()函数中,对props参数进行处理,去除传入的 keyref__self__source属性,这就是为什么在组件中,我们明明传入了 key 和ref,但我们无法通过 this.props.key 或者 this.props.ref 来获取传入的值,就是因为在这里被去除掉了。如果需要用到key,给他一个其他属性,还传递key值。
  • 什么是defaultProps?
    在函数组件或者类组件中,都可以设置defaultProps
// 类组件
class Demo extends Components{
    static defaultPoprs = { name: 'lily' }
}
// 函数组件
function Demo(id){
    return <div id={id}>函数组件</div>
}
Demo.defaultProps={ id : 'lili'}
  1. 进入ReactElement()函数
    返回一个React元素,React 会通过读取这些对象,使用它们构建和更新 DOM
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; 
const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // This tag allows us to uniquely identify this as a React Element防止受xss攻击
    // react元素的节点类型
    $$typeof: REACT_ELEMENT_TYPE, 
    
    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner,
  };
  return element
}

参考文章: juejin.cn/post/716098…