React源碼解析

  • 生命週期

 當首次掛載組件時,按順序執行 getDefaultProps、getInitialState、componentWillMount、 render 和 componentDidMount。
 當卸載組件時,執行 componentWillUnmount。
 當從新掛載組件時,此時按順序執行 getInitialState、componentWillMount、render 和 componentDidMount,但並不執行 getDefaultProps。
 當再次渲染組件時,組件接受到更新狀態,此時按順序執行 componentWillReceiveProps、 shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdatejavascript

  1. 建立自定義組件

class MyComponent extends React.Component 其實就 是調用內部方法 createClass 建立組件
調用getDefaultProps。
因爲 getDefaultProps 是經過構造函數進行管理的,因此也是整個生命週期中先開始執行的java

  1. MOUNTING(加載組件)

若存在 componentWillMount,則執行。若是此時在 componentWillMount 中調用 setState 方法,不會觸發re-render,會進行state合併,而且this.state不是最新的。算法

  1. RECEIVE_PROPS(更新組件)

若是此時在 componentWillReceiveProps 中調 用 setState,是不會觸發 re-render的,而是會進行 state 合併。
updateComponent 本質上也是經過遞歸渲染內容的,因爲遞歸的特性,父組件的 component- WillUpdate 是在其子組件的 componentWillUpdate 以前調用的,而父組件的 componentDidUpdate 也是在其子組件的 componentDidUpdate 以後調用的
禁止在 shouldComponentUpdate 和 componentWillUpdate 中調用 setState,這會形成循環調用,直至耗光瀏覽器內存後崩潰數組

  1. UNMOUNTING(卸載組件)

若是存在 componentWillUnmount,則執行並重置全部相關參數、更新隊列以及更新狀態,如 果此時在 componentWillUnmount 中調用 setState,是不會觸發 re-render 的,這是由於全部更新 隊列和更新狀態都被重置爲 null瀏覽器

無狀態組件
只有一個render方法app


  • setState

調用setState -> 新state進入隊列 -> 合併更新隊列 -> 判斷是否在批量更新 -> 若是在,component放入dirtyComponent等待下一次更新;若是不在,進行批量更新函數

ReactComponent.prototype.setState = function(partialState, callback) {
  this.updater.enqueueSetState(this, partialState); //更新state
  if (callback) {//回調函數
    this.updater.enqueueCallback(this, callback, 'setState');
  }
 };

enqueueSetState中,合併更新隊列,調用enqueueUpdatethis

function enqueueUpdate(component) {
  // 不處於批量更新模式,進行更新
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component); //批處理更新
    return;
  }
  // 處於批量更新模式
  dirtyComponents.push(component);
 }

  • 事務(Transaction)
    事務就是將須要執行的方法使用 wrapper 封裝起來,再經過事務提供的 perform 方法執行。 而在 perform 以前,先執行全部 wrapper 中的 initialize 方法,執行完 perform 以後(即執行 method 方法後)再執行全部的 close 方法。一組 initialize 及 close 方法稱爲一個 wrapper。
    而要使用事務的模 塊,除了須要把 mixin 混入本身的事務實現中外,還要額外實現一個抽象的 getTransactionWrappers 接口。這個接口用來獲取全部須要封裝的前置方法(initialize)和收尾方法(close), 所以它須要返回一個數組的對象,每一個對象分別有 key 爲 initialize 和 close 的方法prototype

    perform (func, scope,a,b,c,d,e,f){
      this.initializeAll(0);
      method.call(scope, a, b, c, d, e, f);
      this.closeAll(0);
    }
  • Diff算法

Diff算法本質上是對javascript對象之間的比較,只有在React更新階段(調用了setState)纔會有Diff算法的運用。
流程:code

  • this.setState(partialState) 更新state
  • this.replaceState(completeState) 合併state
  • this._receivePropsAndState(this.props,nextState,transaction)收到新的props和state,決定是否更新組件

_receivePropsAndState: function(nextProps, nextState, transaction)

if (!this.shouldComponentUpdate ||  //沒有定義shouldComponentUpdate函數
this.shouldComponentUpdate(nextProps, nextState)) {  //shouldComponentUpdate函數返回true
  this._performComponentUpdate(nextProps, nextState, transaction);
} else {  
//shouldComponentUpdate函數返回了false,不進行DOM更新,只更新props和state的值
  this.props = nextProps;
  this.state = nextState;
}

4.this._performComponentUpdate(nextProps, nextState, transaction);
調用this.componentWillUpdate
this.updateComponent(transaction);
調用this.componentDidUpdate

5.this.updateComponent(transaction)

updateComponent: function(transaction) {
var currentComponent = this._renderedComponent;  //原組件
var nextComponent = this._renderValidatedComponent();  //新組件
//兩個組件的類相同(構造函數同樣)
if (currentComponent.constructor === nextComponent.constructor) {
  if (!nextComponent.props.isStatic) {
    currentComponent.receiveProps(nextComponent.props, transaction); //更新組件
  }
} else {
  // 兩個組件的類不同,直接替換
  var thisID = this._rootNodeID;
  var currentComponentID = currentComponent._rootNodeID;
  //卸載原組件
  currentComponent.unmountComponent();
  //加載新組件
  var nextMarkup = nextComponent.mountComponent(thisID, transaction);
  ReactComponent.DOMIDOperations.dangerouslyReplaceNodeWithMarkupByID(
    currentComponentID,
    nextMarkup
  );
  this._renderedComponent = nextComponent;
}

}

  1. currentComponent.receiveProps(nextComponent.props, transaction)

有三種類型的component:
①文本 ReactTextComponent

receiveProps: function(nextProps, transaction) {
//text不同直接替換
if (nextProps.text !== this.props.text) {
  this.props.text = nextProps.text;
  ReactComponent.DOMIDOperations.updateTextContentByID(
    this._rootNodeID,
    nextProps.text
  );
}
}

②React自定義組件
調用componentWillReceiveProps
再次調用this._receivePropsAndState(nextProps, nextState, transaction);

tree diff
比較兩棵DOM樹,若是某個節點不存在,則該節點及其子節點會被徹底刪除,不會進一步比較。 React只會簡單地考慮同層級節點的位置變換
component diff
若是是同一類型組件,繼續比較
若是不是,替換整個組件
對於同一類型的組件,有可能其 Virtual DOM 沒有任何變化,若是可以確切知道這點,那 麼就能夠節省大量的 diff 運算時間。所以,React 容許用戶經過 shouldComponentUpdate() 來判斷該組件是否須要進行 diff 算法分析。
element diff
1.比較新舊集合元素的key,若是有相同key,說明舊集合中有新集合的元素。
2.若是該元素在舊集合的index < lastIndex (lastindex指的是訪問過的元素在舊集合中最大的index),移動該元素到nextIndex,不然不移動。
3.若是新集合裏的元素在舊集合不存在,建立新元素到當前index。
4.更新lastIndex, nextIndex++

存在的缺陷《深刻React技術棧》做者觀點是:若是舊集合是A,B,C,D, 新集合是D,A,B,CD不會移動,而ABC都要依次移動。實際上D只要移動到C後面本人認爲:A,B,C的index都已經發生變化,因此確定會有移動操做,避免不了。

相關文章
相關標籤/搜索