7

前言

继续上一篇,还有4个API:
createElement,
cloneElement
createFactory
isValidElement

createElement


  <div className="class" props="props" ref="ref" key="key">
    <p>1</p>
    <p>2</p>
  </div>,

上面这段jsx代码通过babel转换后如下

React.createElement("div", {
  className: "class",
  props: "props",
  ref: "ref",
  key: "key"
}, React.createElement("p", null, "1"), React.createElement("p", null, "2"));
  • 可以看到转换后会调用createElement函数
  • 第一个参数是个domElement 字符串
  • 第二个参数是个把标签上的属性转换成了一个对象
  • 后面的参数也是createElement函数,分别是两个包裹在div中的p标签的创建,也就是children
// 不加入到props对象中的属性
const RESERVED_PROPS = {
  key: true,
  ref: true,
  __self: true,
  __source: true
};

export function createElement(type, config, ...children) {
  let propName;
  const props = {};
  let key = null;
  let ref = null;

  if (config !== null) {
    key = config.key || null;
    ref = config.ref || null;
  }

  if (config != null) {
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
  }

  // default props, 比如class App上挂了 App.defaultProps = {} 时的处理
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (!props[propName]) {
        props[propName] = defaultProps[propName];
      }
    }
  }

  // react-dom 首次渲染 没有children参数
  props.children =
    children.length === 0
      ? null
      : children.length === 1
      ? children[0]
      : children;

  return ReactElement(type, key, ref, props);
}
  • 拿到config中的key和ref分别赋值给声明好的key和ref
  • 遍历config中的属性,根据RESERVED_PROPS对象过滤不需要的属性,将剩下的属性全部保存到props这个对象中
  • 将default中的属性拷贝到props中
  • 保存children到props中
  • 调用ReactElement
const ReactElement = (type, key, ref, props) => {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE,
    type,
    ref,
    key,
    props: props
  };

  return element;
};
  • 返回一个element对象
  • `$$`typeof: 一个symbol标识,标识是什么类型的element,和前面的一些API里的稍有不通,前面一些API中的$$typeof是保存在type当中
  • type:createElement的第二个参数
  • ref:在createElement中提取的ref
  • key:在createElement中提取的key
  • props:在createElement中处理后的props

cloneElement

export function cloneElement(element, config, ...children) {
  let propName;

  const props = Object.assign({}, element.props);

  let key = element.key;
  let ref = element.ref;

  if (config != null) {
    if (config.ref !== undefined) {
      ref = config.ref;
    }
    if (config.key !== undefined) {
      key = "" + config.key;
    }

    let defaultProps;
    if (element.type && element.type.defaultProps) {
      defaultProps = element.type.defaultProps;
    }
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        if (config[propName] === undefined && defaultProps !== undefined) {
          // Resolve default props
          props[propName] = defaultProps[propName];
        } else {
          props[propName] = config[propName];
        }
      }
    }

    props.children =
      children.length === 0
        ? null
        : children.length === 1
        ? children[0]
        : children;

    return ReactElement(element.type, key, ref, props);
  }
}

cloneElement的实现和createElement几乎一样,最后return的还是一个ReactElement对象,只不过第一个参数不同,第一个参数接收的是一个ReactElement,也就是createElement返回的那个对象

createFactory

export function createFactory(type) {
  const factory = createElement.bind(null, type);
  factory.type = type;

  return factory;
}
  • 返回一个createElement(type) 函数

这个API好像已经没用了,也用不到

isValidElement

/**
 * 验证是否是react对象,主要是通过对象上的$$typeof属性
 * @param {*} object
 */
export function isValidElement(object) {
  return (
    typeof object === "object" &&
    object !== null &&
    object.$$typeof === REACT_ELEMENT_TYPE
  );
}
  • 一个验证是否是react对象的函数,主要是通过对象上的$$typeof属性

react这个包基本写完了


accord
1.3k 声望187 粉丝

希望遇到一个公司,遇到一个团队,大家都愿意把code当作一种艺术去书写