react 源碼解析 2-1 react-api

目錄解析

  1. packages/events 事件系統
  2. packages/react
  3. packages/react-dom
  4. packages/react-reconciler
  5. packages/scheduler

jsx 到 javascript轉換過程

看下babel編譯結果javascript

首字母是不是大小寫 判斷翻譯成字符串仍是變量組件的原理 html

createElement

源碼文件目錄 java

createElement有三個參數 type, config, children。 點擊查看api 文檔
實現的主要思路是: 點擊進入下文代碼 demo 在線編輯地址react

  1. 提取props,將key,ref,__self,__source 賦值
if (config != null) {
    // 剔除config中的 key ref self source 屬性 ,並賦予值
    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;

    // 將properties 添加到新的props對象中
    // 使用hasOwnProperty.call 爲防止 config對象自定義hasOwnProperty 屬性 
    // 具體解釋點擊 https://www.jianshu.com/p/95839681776d
    // 未知對象用 hasOwnProperty.call 已知對象能夠直接使用 hasOwnProperty
    for(propName in config){
      if(
        hasOwnProperty.call(config,propName) && 
        !RESERVED_PROPS.hasOwnProperty(propName)
      ){
        props[propName] = config[propName];
      }
    }
  }
複製代碼
  1. Children can be more than one argument, and those are transferred onto the newly allocated props object
// children 處理獲得 props.children
    const childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
      props.children = children;
    } else if (childrenLength > 1) {
      const childrenArray = Array(childrenLength);
      for (let i = 0; i < childrenLength; i++) {
        childrenArray[i] = arguments[i + 2];
      }
      // freeze 凍結一個對象使其不可修改 且freeze爲淺凍結
      // mdn 地址 : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
      if (__DEV__) {
        if (Object.freeze) {
          Object.freeze(childrenArray);
        }
      }
      props.children = childrenArray;
    }
複製代碼
  1. 處理 defaultProps
// 若是是組件
    if (type && type.defaultProps) {
      const defaultProps = type.defaultProps;
      for (propName in defaultProps) {
        if (props[propName] === undefined) {
          props[propName] = defaultProps[propName];
        }
      }
    }
複製代碼
  1. __DEV__環境下key,ref處理
// __DEV__下warning 在displayName 組件中 key,ref 是不可配置的屬性
    if(__DEV__){
      if(key || ref){
        const displayName = 
        typeof type === 'function' 
        ? type.displayName || type.name || 'Unknown'
        : type;
        if (key) {
          defineKeyPropWarningGetter(props, displayName);
        }
        if (ref) {
          defineRefPropWarningGetter(props, displayName);
        }
      }
    }
複製代碼
  1. 返回結果
return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
複製代碼

ReactElement

ReactElement 接收type, key, ref, self, source, owner, props 7各參數api

slef:
當React.createElement被調用時,用來檢測this的來源,以便咱們能夠發出警告。咱們想要擺脫全部者並用箭頭函數替換字符串refs,只要this和全部者是相同的就沒有了改變行爲 source:
一個註解對象 (由轉路器或者其餘人添加)。用來判斷兩個react element元素是否相等 owner:
記錄負責建立此元素的組件數組

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // $$typeof是區分react Element對象的惟一標識
    $$typeof: REACT_ELEMENT_TYPE,

    // 屬於元素的內置屬性
    type: type,
    key: key,
    ref: ref,
    props: props,

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

  if (__DEV__) {
    element._store = {};

    Object.defineProperty(element._store, 'validated', {
      configurable: false,
      enumerable: false,
      writable: true,
      value: false,
    });
    // self and source are DEV only properties.
    Object.defineProperty(element, '_self', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: self,
    });
    // 爲了測試目的,在兩個不一樣的地方建立的兩個元素應被視爲相等,所以咱們將其隱藏在枚舉中
    Object.defineProperty(element, '_source', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: source,
    });
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }
  }

  return element;
};

複製代碼

React Component

React Component 只是用來幫助咱們承載一些信息的,沒有生命週期等功能的實現promise

function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};
複製代碼

Componenet的prototype 掛載方法bash

  1. setState
  2. forceUpdate

PureComponent

PureComponent 進行的 淺比較 (shallow equality check)babel

function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
// PureComponent 與 Component 本文件中代碼上區別是
pureComponentPrototype.isPureReactComponent = true;
複製代碼

ref

三種使用ref的方式dom

  1. sting ref
  2. method ref
  3. object ref
    代碼不多 只有這麼點

createRef

import type {RefObject} from 'shared/ReactTypes';

// an immutable object with a single mutable value
export function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}
複製代碼

forwardRef

forwardRef 解決了 pure Function 沒有ref實例的問題
forwardRef 返回 React$Node
官網api文檔 Refs 轉發
點擊進入 : 在線代碼demo編輯

Suspense && lazy

點擊進入 Suspense && lazy 使用demo 連接

import React, { Suspense } from "react";
import ReactDOM from "react-dom";
const LazyComp = React.lazy(() => import("./lazy"));

let data = "";
let promise = "";
const requestData = () => {
  if (data) return data;
  if (promise) throw promise;
  promise = new Promise(res => {
    setTimeout(() => {
      data = "data res";
      res();
    }, 2000);
  });
  throw promise;
};

const SuspenseComp = () => {
  const data = requestData();
  return <div>{data}</div>;
};
const Comp = () => {
  return (
    <Suspense fallback="loading data"> <SuspenseComp /> <LazyComp /> </Suspense>
  );
};

ReactDOM.render(<Comp />, document.getElementById("container")); 複製代碼

Suspense只有等內部全部組件都加載完畢,纔會把fallback的內容去掉,有任何一個組件處於pending狀態,都會加載fallback

Children

點擊 進入下文代碼在線編輯demo

React.children.map把多維數組A轉一維數B,props.children的每一項在數組B中遍歷

下文源代碼中 result

function mapChildren(children, func, context) {
  if (children == null) {
    return children;
  }
  const result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, func, context);
  return result;
}
複製代碼
相關文章
相關標籤/搜索