react源碼學習(一)從JSX到React element

一. 體驗jsx: https://babeljs.io/repl

const App = () => {
  return <div id="app" key="key">App</div>
}
//轉化結果
"use strict";

var App = function App() {
  return React.createElement("div", null, "App");
};

注意到的是我們的JSX最終轉化成爲的是React.createElement這個方法:

  • 第一個參數是字符串類型或者組件或者symbol,
    • 代表的是標籤元素, 如div, span
    • classComponent或者是functional Component,
    • 原生提供的Fragment, AsyncMode等, 會被特殊處理
  • 第二個參數是一個對象類型, 代表標籤的屬性, id, class
  • 其餘的參數代表的是children,不涉及grand-children,當子節點中有孫節點的時候, 再遞歸使用React.createElement方法
const App = () => {
  return <div id="app" key="key">
    <section>
    	<img />
    </section>
    <span>span</span>
  </div>
}

"use strict";

var App = function App() {
  return React.createElement(
      "div", 
      {id: "app",key: "key"}, 
      React.createElement("section", null, 
              React.createElement("img", null)
          ), 
      React.createElement("span", null, "span"));
};

當第一個參數是組件的時候,第一個參數是作爲變量傳入的, 可以想像的是, React.createElement內部有一個簡單的判斷, 如果傳入的是組件的話, 內部還會調用React.createElement方法

const Child = () => {
	return <div>Child</div>
}
const App = () => {
  return <div id="app">
    <Child />
  </div>
}

"use strict";

var Child = function Child() {
  return React.createElement("div", null, "Child");
};

var App = function App() {
  return React.createElement(
      "div", 
      {id: "app"}, 
      React.createElement(Child, null)); //這裏
};

需要注意的是如果組件開頭是一個小寫的話, 會被解析成簡單的字符串,在運行的時候就會報錯

二. React.createElement
我們的createElement方法定義在packages/src/ReactElement.js

export function createElement(type, config, children) {
  let propName;
  const props = {};
  
  let key = null;
  let ref = null;
  let self = null;
  let source = null;
   
   // ref和key和其他props不同, 進行單獨處理
  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];
      }
    }
  }
  //第三個及以上的參數都被看作是子節點
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    //當數組長度確定時,這種方式比push要節省內存  
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray; 
  // merge defaultProps
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

ReactElement定義如下

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // 每個react element的$$typeof屬性都是REACT_ELEMENT_TYPE
    $$typeof: REACT_ELEMENT_TYPE, // react element的內置屬性
    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner, //創建該元素的component
  };
  return element;
};

總的來說就是返回一個react element, 該element帶有props, refs, type

3. component和pureComponent

和React.Component相關的文件放在react/ReactBaseClasses.js中
updater是和平臺相關的,react dom 平臺和react native平臺setState後需要走的流程都是不一樣的

function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  //初始化refs和updater,後面會被覆寫
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};

常用的setState方法

Component.prototype.setState = function(partialState, callback) {
  invariant( //錯誤提醒
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null,
    'setState(...): takes an object of state variables to update or a ' +
      'function which returns an object of state variables.',
  );
  //重點代碼
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

Component.prototype.isReactComponent = {};

PureComponent只是實現了對Component的簡單繼承(詳情參加<高程>一書),並且添加了isPureReactComponent屬性

function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;
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;
Object.assign(pureComponentPrototype, Component.prototype);

pureComponentPrototype.isPureReactComponent = true;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章