React 源碼解析之ReactElement

ReactElement.js 總體部分

// packages/react/src/ReactElement.js
// 保留的 props
const RESERVED_PROPS = {
  key: true,
  ref: true,
  __self: true,
  __source: true,
};

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // 此標記容許咱們將其惟一標識爲React元素
    $$typeof: REACT_ELEMENT_TYPE,
    
    // 屬於元素的內置屬性
    type: type,
    key: key,
    ref: ref,
    props: props,

    // 記錄負責建立此元素的組件。
    _owner: owner,
  };

  if (__DEV__) {/*...*/}

  return element;
} 

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];
      }
    }
  }

  const childrenLength = arguments.length - 2;
  if( childrenLength === 1) {
    props.children = children;
  } else {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    if (__DEV__) { /*...*/ }
    props.children = childArray;
  }

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

解析

1.ReactElement 是經過 createElement 函數建立;javascript

2.createElement 函數接收 3 個參數,分別是 type, config, children, type 指的是 ReactElement 的類型;java

type 類型有react

  • 字符串 divp 等表明原生 DOM,稱爲 HostComponent;
  • class 類型繼承 ComponentPureComponent 組件的稱爲 ClassComponent;
  • 方法就是 FunctionalComponent;
  • 原生提供的 FragmentAsyncMode 等是 Symbol,會特殊處理;
  • 其餘;

3.調用 ReactElementReactElement 內部返回一個對象 element數組

config 邏輯

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;
  // 剩餘的屬性將添加到新的 props 對象中
  for (propName in config) {
    if(hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
      props[propName] = config[propName];
    }
  }
}
複製代碼

這段代碼函數

  • 建立的時候都是從 config 傳入的,refkey 不會和 config 中的變量一塊兒被處理,而是單獨做爲變量出如今 ReactElement 上;
  • refkey 作了驗證(對於這種校驗方法無需內部實現,保持乾淨,也便於閱讀);
  • 遍歷 config 把內建的幾個屬性(剔除原型鏈上的屬性和規定要剔除的屬性)丟到 props 中去;

children 邏輯

const childrenLength = arguments.length - 2;
if( childrenLength === 1) {
  props.children = children;
} else {
  const childArray = Array(childrenLength);
  for (let i = 0; i < childrenLength; i++) {
    childArray[i] = arguments[i + 2];
  }
  if (__DEV__) { /*...*/ }
  props.children = childArray;
}
複製代碼

第一行能夠看出,取出第二個參數後的參數,而後判斷長度是否 > 1:post

  • > 1 就表明有多個 children,這個時候 props.children 會是一個數組, 因此後面在對 props.children 進行遍歷的時候須要注意它是不是數組,固然你也能夠利用 React.Children 中的 API,下文中也會對 React.Children 中的 API 進行講解;
  • === 1 就是一個對象;

返回值

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // 此標記容許咱們將其惟一標識爲React元素
    $$type: REACT_ELEMENT_TYPE,
    
    // 屬於元素的內置屬性
    type: type,
    key: key,
    ref: ref,
    props: props,

    // 記錄負責建立此元素的組件。
    _owner: owner,
  };

  if (__DEV__) {/*...*/}

  return element;
} 
複製代碼

核心就是經過 $$type 來識別這是個 ReactElement,後會看到不少相似的類型。ui

注意:經過 JSX 寫的 <APP /> 表明 ReactElementAPP 表明 React Component (組件)。spa

這章節的內容能夠繪製成的流程

createElement 流程

你能夠...

上一篇:React 源碼解析之總覽code

下一篇:React 源碼解析之React.Childrencdn

相關文章
相關標籤/搜索