React源碼簡析(版本React-15.2.0)

前言:

         用了這麼久的React,是時候啃一下源碼了,原本想直接上React16的源碼,發現加入React Fiber後,看起來很是痛苦.沒辦法只能先啃老的,再不學就跟不上了!javascript

收拾下心情,進入正題以前,先講一下React的難點事務,React中存在大量事務的使用html

1. transaction事務

先看一下事務的代碼實現:java

var Transaction = {
    reinitializeTransaction: function() {
        this.transactionWrappers = this.getTransactionWrappers();
        if (this.wrapperInitData) {
            this.wrapperInitData.length = 0;
        } else {
            this.wrapperInitData = [];
        }
        this._isInTransaction = false;
    },
    _isInTransaction: false,
    getTransactionWrappers: null,
    perform: function(method, scope, a, b, c, d, e, f) {
        var ret;
        try {
            this._isInTransaction = true;
            this.initializeAll(0);
            ret = method.call(scope, a, b, c, d, e, f);
        } finally {
            this.closeAll(0);
            this._isInTransaction = false;
        }
        return ret;
    },
    initializeAll: function() {
        var transactionWrappers = this.transactionWrappers;
        for (var i = startIndex; i < transactionWrappers.length; i++) {
            var wrapper = transactionWrappers[i];
            this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
        }
    },
    closeAll: function(startIndex) {
        var transactionWrappers = this.transactionWrappers;
        for (var i = startIndex; i < transactionWrappers.length; i++) {
            var wrapper = transactionWrappers[i];
            var initData = this.wrapperInitData[i];
            wrapper.close.call(this, initData);
        }
        this.wrapperInitData.length = 0;
    }
};複製代碼

舉個例子:node

function update(){
    console.log('更新了')
};
Transaction.perform(update);
//會在執行update方法前,先執行initializeAll,循環調用initialize方法
//輸出'更新了'
//執行closeAll,循環調用transactionWrappers中的close方法複製代碼

這個事務只是抽象了功能getTransactionWrappers爲null,具體的執行內容要求子類實現:react

也就是說在方法執行先後分別作一些事情!api

下面咱們看下ReactReconcileTransaction(協調事務實現)數組

var SELECTION_RESTORATION = {
    initialize: ReactInputSelection.getSelectionInformation,
    close: ReactInputSelection.restoreSelection
};
var EVENT_SUPPRESSION = {
    initialize: function() {
        var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
        ReactBrowserEventEmitter.setEnabled(false);
        return currentlyEnabled;
    },
    close: function(previouslyEnabled) {
        ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
    }
};
var ON_DOM_READY_QUEUEING = {
    initialize: function() {
        this.reactMountReady.reset();
    },
    close: function() {
        this.reactMountReady.notifyAll();
    }
};
var TRANSACTION_WRAPPERS = [SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING];
function ReactReconcileTransaction(useCreateElement) {
    this.reinitializeTransaction();
}
var Mixin = {
    getTransactionWrappers: function() {
        return TRANSACTION_WRAPPERS;
    },
    getUpdateQueue: function() {
        return ReactUpdateQueue;
    }
};
_assign(ReactReconcileTransaction.prototype, Transaction, Mixin);複製代碼


ReactReconcileTransaction繼承了Transaction,重寫了getTransactionWrappers方法

getTransactionWrappers: function () {    return TRANSACTION_WRAPPERS;  },複製代碼
  • SELECTION_RESTORATION :在更新DOM時,收集當前獲取焦點的元素與選區,更新結束後,還原焦點與選區
  • EVENT_SUPPRESSION:在更新前關閉事件響應,更新後打開事件響應
  • ON_DOM_READY_QUEUEING:在dom更新完成後執行一些內容例如componentDidMount

官方源碼的解析圖:瀏覽器


簡單地說,一個Transaction 就是將須要執行的 method 使用 wrapper 封裝起來,再經過 Transaction 提供的 perform 方法執行。而在 perform 以前,先執行全部 wrapper 中的 initialize 方法;perform 完成以後(即 method 執行後)再執行全部的 close 方法。一組 initialize 及 close 方法稱爲一個 wrapper,從上面的示例圖中能夠看出 Transaction 支持多個 wrapper 疊加。
緩存

下面開始進入正題:bash

2.經過一個例子說明整個流程

import React from 'react';
import ReactDOM from 'react-dom';
class HelloWorld extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            message: "hello, world",
            className: 'react-wrap'
        }
    }
    componentWillMount() {
        debugger console.log("component will mount");
    }
    componentWillUpdate() {
        debugger console.log("component will update");
    }
    componentDidUpdate() {
        debugger console.log("component did update");
    }
    componentDidMount() {
        debugger console.log("componentDidMount");
    }
    handleMessage() {
        debugger this.setState({
            message: '哈哈',
            className: 'list-wrap'
        });
    }
    render() {
        return < span className = {
            this.state.className
        }
        onClick = {
            this.handleMessage.bind(this)
        } > {
            this.state.message
        } < /span>  }}ReactDOM.render( <HelloWorld/ > ,
        document.getElementById('screen-check'))

        即: ReactDOM.render(React.createElement(HelloWorld, null), document.getElementById('screen-check'));


複製代碼

<HelloWorld/>轉換後就是 React.createElement(HelloWorld, null);

ReactElement.createElement = function(type, config, children) {
    var propName;
    var props = {};
    var key = null;
    var ref = null;
    var self = null;
    var 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;
        for (propName in config) {
            if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
                props[propName] = config[propName];
            }
        }
    }
    var childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
        props.children = children;
    } else if (childrenLength > 1) {
        var childArray = Array(childrenLength);
        for (var i = 0; i < childrenLength; i++) {
            childArray[i] = arguments[i + 2];
        }
        props.children = childArray;
    }
    if (type && type.defaultProps) {
        var 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);
};複製代碼


createElement對參數進行整理後調用ReactElement

返回:


獲得Element,接着調用render


其中:nextElement:<HelloWorld/>   container:document.getElementById('screen-check')
接下來調用
_renderSubtreeIntoContainer,

將傳入的nextElement作了統一包裝nextWrappedElement,這樣能夠保持根組件統一,方便處理

_renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
    var nextWrappedElement = React.createElement(TopLevelWrapper, {
        child: nextElement
    });
    var nextContext;
    if (parentComponent) {
        var parentInst = ReactInstanceMap.get(parentComponent);
        nextContext = parentInst._processChildContext(parentInst._context);
    } else {
        nextContext = emptyObject;
    }
    var prevComponent = getTopLevelWrapperInContainer(container);
    if (prevComponent) {
        var prevWrappedElement = prevComponent._currentElement;
        var prevElement = prevWrappedElement.props.child;
        if (shouldUpdateReactComponent(prevElement, nextElement)) {
            var publicInst = prevComponent._renderedComponent.getPublicInstance();
            var updatedCallback = callback &&
            function() {
                callback.call(publicInst);
            };
            ReactMount._updateRootComponent(prevComponent, nextWrappedElement, nextContext, container, updatedCallback);
            return publicInst;
        } else {
            ReactMount.unmountComponentAtNode(container);
        }
    }
    var reactRootElement = getReactRootElementInContainer(container);
    var containerHasReactMarkup = reactRootElement && !!internalGetID(reactRootElement);
    var containerHasNonRootReactChild = hasNonRootReactChild(container);
    var shouldReuseMarkup = containerHasReactMarkup && !prevComponent && !containerHasNonRootReactChild;
    var component = ReactMount._renderNewRootComponent(nextWrappedElement, container, shouldReuseMarkup, nextContext)._renderedComponent.getPublicInstance();
    if (callback) {
        callback.call(component);
    }
    return component;
},
複製代碼

首次掛載,prevComponent不存在,直接調用_renderNewRootComponent

ReactMount._renderNewRootComponent(nextWrappedElement, container, shouldReuseMarkup, nextContext)複製代碼
nextWrappedElement結構:

_renderNewRootComponent: function(nextElement, container, shouldReuseMarkup, context) {
    var componentInstance = instantiateReactComponent(nextElement, false);
    ReactUpdates.batchedUpdates(batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context);

    var wrapperID = componentInstance._instance.rootID;
    instancesByReactRootID[wrapperID] = componentInstance;

    return componentInstance;
},
複製代碼

instantiateReactComponent()實例化組件根據ReactElement中不一樣的type字段,建立不一樣類型的組件對象。源碼以下

    • // 初始化組件對象,node是一個ReactElement對象,是節點元素在React中的表示
      function instantiateReactComponent(node) {
        var instance;
      
        var isEmpty = node === null || node === false;
        if (isEmpty) {
          // 空對象
          instance = ReactEmptyComponent.create(instantiateReactComponent);
        } else if (typeof node === 'object') {
          // 組件對象,包括DOM原生的和React自定義組件
          var element = node;
      
          // 根據ReactElement中的type字段區分
          if (typeof element.type === 'string') {
            // type爲string則表示DOM原生對象,好比div span等。能夠參看上面babel轉譯的那段代碼
            instance = ReactNativeComponent.createInternalComponent(element);
          } else if (isInternalComponentType(element.type)) {
            // 保留給之後版本使用,此處暫時不會涉及到
            instance = new element.type(element);
          } else {
            // React自定義組件
            instance = new ReactCompositeComponentWrapper(element);
          }
        } else if (typeof node === 'string' || typeof node === 'number') {
          // 元素是一個string時,對應的好比<span>123</span> 中的123
          // 本質上它不是一個ReactElement,但爲了統一,也按照一樣流程處理,稱爲ReactDOMTextComponent
          instance = ReactNativeComponent.createInstanceForText(node);
        } else {
          // 無處理
        }
      
        // 初始化參數,這兩個參數是DOM diff時用到的
        instance._mountIndex = 0;
        instance._mountImage = null;
      
        return instance;
      }複製代碼
      • ReactEmptyComponent.create(), 建立空對象ReactDOMEmptyComponent
      • ReactNativeComponent.createInternalComponent(), 建立DOM原生對象
      • ReactDOMComponent new ReactCompositeComponentWrapper(), 建立React自定義對象ReactCompositeComponent
      • ReactNativeComponent.createInstanceForText(), 建立文本對象 ReactDOMTextComponent
node 實際參數 結果
null/false 建立ReactEmptyComponent組件
object && type === string 虛擬DOM 建立ReactDOMComponent組件
object && type !== string React組件 建立ReactCompositeComponent組件
string 字符串 建立ReactTextComponent組件
number 數字 建立ReactTextComponent組件


var ReactCompositeComponent = {
    construct: function(element) {
        this._currentElement = element;
        this._rootNodeID = 0;
        this._compositeType = null;
        this._instance = null;
        this._hostParent = null;
        this._hostContainerInfo = null;
        this._updateBatchNumber = null;
        this._pendingElement = null;
        this._pendingStateQueue = null;
        this._pendingReplaceState = false;
        this._pendingForceUpdate = false;

        this._renderedNodeType = null;
        this._renderedComponent = null;
        this._context = null;
        this._mountOrder = 0;
        this._topLevelWrapper = null;
        this._pendingCallbacks = null;
        this._calledComponentWillUnmount = false;
    },
    mountComponent: function(transaction, hostParent, hostContainerInfo, context) {}
    
    ...
}複製代碼
componentInstance結構:

batchedUpdates批量更新

ReactUpdates.batchedUpdates(batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context);複製代碼

function batchedUpdates(callback, a, b, c, d, e) {
  return batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}複製代碼

查看batchingStrategy代碼:

var RESET_BATCHED_UPDATES = {
    initialize: emptyFunction,
    close: function() {
        ReactDefaultBatchingStrategy.isBatchingUpdates = false;
    }
};

var FLUSH_BATCHED_UPDATES = {
    initialize: emptyFunction,
    close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

function ReactDefaultBatchingStrategyTransaction() {
    this.reinitializeTransaction();
}
_assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
    getTransactionWrappers: function() {
        return TRANSACTION_WRAPPERS;
    }
});
var transaction = new ReactDefaultBatchingStrategyTransaction();

var ReactDefaultBatchingStrategy = {
    isBatchingUpdates: false,
    batchedUpdates: function(callback, a, b, c, d, e) {
        var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

        ReactDefaultBatchingStrategy.isBatchingUpdates = true;
        if (alreadyBatchingUpdates) {
            return callback(a, b, c, d, e);
        } else {
            return transaction.perform(callback, null, a, b, c, d, e);
        }
    }
};

module.exports = ReactDefaultBatchingStrategy;複製代碼

ReactDefaultBatchingStrategy.batchedUpdates內容使用了開始提到的transaction

在執行batchedMountComponentIntoNode 前 先執行RESET_BATCHED_UPDATES,FLUSH_BATCHED_UPDATESinitialize方法,這裏是個空函數

接着進入事務調用

transaction.perform(callback, null, a, b, c, d, e);複製代碼

這是一個事務嵌套:

執行過程以下圖:


根據上面的事務詳細圖可知道

  1. ReactDefaultBatchingStrategy中
  • FLUSH_BATCHED_UPDATES.initialize爲空函數
  • RESET_BATCHED_UPDATES.initialize爲空函數

ReactReconcileTransaction事務中

  • SELECTION_RESTORATION.initialize獲取更新前焦點元素,方便更新後恢復(好比input聚焦)
  •  EVENT_SUPPRESSION.initialize,關閉事件響應
  • ON_DOM_READY_QUEUEING,初始化onDOMReady列表

到此先總結一下:

接下來通過兩個事務嵌套,執行

function mountComponentIntoNode(wrapperInstance, container, transaction, shouldReuseMarkup, context) {
    
      // 調用對應中的ReactCompositeComponent 中mountComponent方法來渲染組件。
      // mountComponent返回React組件解析的HTML。不一樣的ReactComponent的mountComponent策略不一樣
    var markup = ReactReconciler.mountComponent(wrapperInstance, transaction, null, ReactDOMContainerInfo(wrapperInstance, container), context, 0
    /* parentDebugID */
    );
    wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;

    // 將解析出來的HTML插入DOM中
    ReactMount._mountImageIntoNode(markup, container, wrapperInstance, shouldReuseMarkup, transaction);
}複製代碼

進入mountComponentIntoNode先看markup 調用ReactReconciler.mountComponent

mountComponent: function (internalInstance, transaction, hostParent, hostContainerInfo, context, parentDebugID){
    var markup = internalInstance.mountComponent(transaction, hostParent, hostContainerInfo, context, parentDebugID);
    return markup;
  }複製代碼

ReactReconciler.mountComponent中使用的是對應組件的實例調用本身的mountComponent方法

這裏的實例internalInstance對應的是ReactCompositeComponent類型實例

var ReactCompositeComponent = {
    construct: function(element) {
        //...省略
    },
    mountComponent: function(transaction, hostParent, hostContainerInfo, context) {
        var _this = this;
        this._context = context;
        this._mountOrder = nextMountID++;
        this._hostParent = hostParent;
        this._hostContainerInfo = hostContainerInfo;

        var publicProps = this._currentElement.props;
        var publicContext = this._processContext(context);

        var Component = this._currentElement.type;

        var updateQueue = transaction.getUpdateQueue();

        // Initialize the public class
        var doConstruct = shouldConstruct(Component);
        var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
        var renderedElement;

        if (!doConstruct && (inst == null || inst.render == null)) {
            renderedElement = inst;
            warnIfInvalidElement(Component, renderedElement); ! (inst === null || inst === false || React.isValidElement(inst)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.', Component.displayName || Component.name || 'Component') : _prodInvariant('105', Component.displayName || Component.name || 'Component') : void 0;
            inst = new StatelessComponent(Component);
            this._compositeType = CompositeTypes.StatelessFunctional;
        } else {
            if (isPureComponent(Component)) {
                this._compositeType = CompositeTypes.PureClass;
            } else {
                this._compositeType = CompositeTypes.ImpureClass;
            }
        }
        inst.props = publicProps;
        inst.context = publicContext;
        inst.refs = emptyObject;
        inst.updater = updateQueue;
        this._instance = inst;
        ReactInstanceMap.set(inst, this);
        var initialState = inst.state;
        if (initialState === undefined) {
            inst.state = initialState = null;
        } ! (typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.state: must be set to an object or null', this.getName() || 'ReactCompositeComponent') : _prodInvariant('106', this.getName() || 'ReactCompositeComponent') : void 0;

        this._pendingStateQueue = null;
        this._pendingReplaceState = false;
        this._pendingForceUpdate = false;

        var markup;
        markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);

        if (inst.componentDidMount) {
            transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
        }
        return markup;
    }
}複製代碼

這裏先看一下this._processContext(context);

_processContext: function(context) {
    var maskedContext = this._maskContext(context);
    return maskedContext;
},複製代碼

在使用ContextAPI時,要在自定義組件中定義:

從代碼中能夠看到若是組件不定義contextTypes,返回的Context就是{},取不到值



接着是建立實例

var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);

_constructComponent: function (doConstruct, publicProps, publicContext, updateQueue) {
      return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue);
  },複製代碼

_constructComponentWithoutOwner: function (doConstruct, publicProps, publicContext, updateQueue) {
    var Component = this._currentElement.type;
        return new Component(publicProps, publicContext, updateQueue);
  }複製代碼

建立實例的是以前包裝的根組件


接下來是判斷自定義組件的方式:


function isPureComponent(Component) {
  return !!(Component.prototype && Component.prototype.isPureReactComponent);
}複製代碼

都知道自定義組件有兩種形式:

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
或者
class Greeting extends React.PureComponent {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}複製代碼

React.PureComponentReact.Component 很類似。二者的區別在於 React.Component並未實現 shouldComponentUpdate(),而 React.PureComponent 中以淺層對比 prop 和 state 的方式來實現了該函數。

React.PureComponent 中的 shouldComponentUpdate() 僅做對象的淺層比較

獲得this._compositeType類型後用於後面判斷shouldComponentUpdate()要不要作淺層比較

inst.props = publicProps;
inst.context = publicContext;
inst.refs = emptyObject;
inst.updater = updateQueue;//用於setSate和foreUpdate方法調用時使用複製代碼


ReactCompositeComponent實例經過_instance屬性引入TopLevelWrapper根組件實例

TopLevelWrapper組件實例經過_reactInternalInstance屬性引用ReactCompositeComponent實例

接着初始化掛載

performInitialMount: function(renderedElement, hostParent, hostContainerInfo, transaction, context) {
    var inst = this._instance;
    var debugID = 0;
    if (inst.componentWillMount) {
        inst.componentWillMount();
        // When mounting, calls to `setState` by `componentWillMount` will set
        // `this._pendingStateQueue` without triggering a re-render.
        if (this._pendingStateQueue) {
            inst.state = this._processPendingState(inst.props, inst.context);
        }
    }
    if (renderedElement === undefined) {
        renderedElement = this._renderValidatedComponent();
    }

    var nodeType = ReactNodeTypes.getType(renderedElement);
    this._renderedNodeType = nodeType;
    var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY
    /* shouldHaveDebugID */
    );
    this._renderedComponent = child;
    var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID);
    return markup;
}複製代碼

這裏出現了第一個生命週期componentWillMount

包裝根組件沒有定義componentWillMount跳過.

而後調用render方法


_renderValidatedComponentWithoutOwnerOrContext: function() {
    var inst = this._instance;
    var renderedElement;
    renderedElement = inst.render();
    return renderedElement;
}複製代碼


renderedElement=this._renderValidatedComponent();

返回的就是咱們定義的<HelloWorld/>對象,到此進入本身的世界


接着就是遞歸調用

instantiateReactComponent()實例化組件根據ReactElement中不一樣的type字段,建立不一樣類型的組件對象


根據renderedElement的type Class HelloWorld建立ReactCompositeComponent實例


而後仍是調用ReactCompositeComponent實例的mountComponent方法


此次建立自定義組件HelloWorld


執行HelloWorld對應的ReactCompositeComponent實例初始化掛載時,定義了生命週期


開始調用第一個生命週期



接着調用render

// If not a stateless component, we now render
    if (renderedElement === undefined) {
      renderedElement = this._renderValidatedComponent();
    }複製代碼

renderedElement = this._renderValidatedComponent();


renderedElement的結構爲

接着又是遞歸調用:


此次遞歸不一樣的是renderedElement的type類型是String

根據instantiateReactComponent()實例化組件,此次是ReactDOMComponent組件實例


ReactDOMComponent組件的mountComponent也跟ReactCompositeComponent實例不一樣

這裏包含了建立dom,添加事件,兼容不一樣瀏覽器,是幾種組件最複雜的

ReactDOMComponent = {
    mountComponent: function(transaction, hostParent, hostContainerInfo, context) {
        this._rootNodeID = globalIdCounter++;
        this._domID = hostContainerInfo._idCounter++;
        this._hostParent = hostParent;
        this._hostContainerInfo = hostContainerInfo;

        var props = this._currentElement.props;

        switch (this._tag) {
        case 'audio':
        case 'form':
        case 'iframe':
        case 'img':
        case 'link':
        case 'object':
        case 'source':
        case 'video':
            this._wrapperState = {
                listeners: null
            };
            transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
            break;
        case 'input':
            ReactDOMInput.mountWrapper(this, props, hostParent);
            props = ReactDOMInput.getHostProps(this, props);
            transaction.getReactMountReady().enqueue(trackInputValue, this);
            transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
            break;
        case 'option':
            ReactDOMOption.mountWrapper(this, props, hostParent);
            props = ReactDOMOption.getHostProps(this, props);
            break;
        case 'select':
            ReactDOMSelect.mountWrapper(this, props, hostParent);
            props = ReactDOMSelect.getHostProps(this, props);
            transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
            break;
        case 'textarea':
            ReactDOMTextarea.mountWrapper(this, props, hostParent);
            props = ReactDOMTextarea.getHostProps(this, props);
            transaction.getReactMountReady().enqueue(trackInputValue, this);
            transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
            break;
        }
        var namespaceURI;
        var parentTag;
        if (hostParent != null) {
            namespaceURI = hostParent._namespaceURI;
            parentTag = hostParent._tag;
        } else if (hostContainerInfo._tag) {
            namespaceURI = hostContainerInfo._namespaceURI;
            parentTag = hostContainerInfo._tag;
        }
        if (namespaceURI == null || namespaceURI === DOMNamespaces.svg && parentTag === 'foreignobject') {
            namespaceURI = DOMNamespaces.html;
        }
        if (namespaceURI === DOMNamespaces.html) {
            if (this._tag === 'svg') {
                namespaceURI = DOMNamespaces.svg;
            } else if (this._tag === 'math') {
                namespaceURI = DOMNamespaces.mathml;
            }
        }
        this._namespaceURI = namespaceURI;

        if (process.env.NODE_ENV !== 'production') {
            var parentInfo;
            if (hostParent != null) {
                parentInfo = hostParent._ancestorInfo;
            } else if (hostContainerInfo._tag) {
                parentInfo = hostContainerInfo._ancestorInfo;
            }
            if (parentInfo) {
                validateDOMNesting(this._tag, null, this, parentInfo);
            }
            this._ancestorInfo = validateDOMNesting.updatedAncestorInfo(parentInfo, this._tag, this);
        }

        var mountImage;
        if (transaction.useCreateElement) {
            var ownerDocument = hostContainerInfo._ownerDocument;
            var el;
            if (namespaceURI === DOMNamespaces.html) {
                if (this._tag === 'script') {
                    // Create the script via .innerHTML so its "parser-inserted" flag is
                    // set to true and it does not execute
                    var div = ownerDocument.createElement('div');
                    var type = this._currentElement.type;
                    div.innerHTML = '<' + type + '></' + type + '>';
                    el = div.removeChild(div.firstChild);
                } else if (props.is) {
                    el = ownerDocument.createElement(this._currentElement.type, props.is);
                } else {
                    el = ownerDocument.createElement(this._currentElement.type);
                }
            } else {
                el = ownerDocument.createElementNS(namespaceURI, this._currentElement.type);
            }
            ReactDOMComponentTree.precacheNode(this, el);
            this._flags |= Flags.hasCachedChildNodes;
            if (!this._hostParent) {
                DOMPropertyOperations.setAttributeForRoot(el);
            }
            this._updateDOMProperties(null, props, transaction);
            var lazyTree = DOMLazyTree(el);
            this._createInitialChildren(transaction, props, context, lazyTree);
            mountImage = lazyTree;
        } else {
            var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);
            var tagContent = this._createContentMarkup(transaction, props, context);
            if (!tagContent && omittedCloseTags[this._tag]) {
                mountImage = tagOpen + '/>';
            } else {
                mountImage = tagOpen + '>' + tagContent + '</' + this._currentElement.type + '>';
            }
        }

        switch (this._tag) {
        case 'input':
            transaction.getReactMountReady().enqueue(inputPostMount, this);
            if (props.autoFocus) {
                transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
            }
            break;
        case 'textarea':
            transaction.getReactMountReady().enqueue(textareaPostMount, this);
            if (props.autoFocus) {
                transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
            }
            break;
        case 'select':
            if (props.autoFocus) {
                transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
            }
            break;
        case 'button':
            if (props.autoFocus) {
                transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
            }
            break;
        case 'option':
            transaction.getReactMountReady().enqueue(optionPostMount, this);
            break;
        }

        return mountImage;
    }
}複製代碼

ReactDOMComponent組件的mountComponent乾的那些事

這裏先建立span標籤:


接着在ReactDOMComponent對應的實例屬性_hostNode 引用span節點

在dom節點span添加"__reactInternalInstance$r5bs9lz1m3"屬性引用ReactDOMComponent對應的實例,internalInstanceKey的值爲"__reactInternalInstance$r5bs9lz1m3"


接下來在dom節點上添加屬性

this._updateDOMProperties(null, props, transaction);

  • 第一個遍歷處理的是className屬性


在DOMProperty.properties中找到'className'對應的屬性名稱爲class,在dom節點上設置classs屬性


  • 下一個處理的是"onClick",開始註冊事件監聽:

function enqueuePutListener(inst, registrationName, listener, transaction) {
    var containerInfo = inst._hostContainerInfo;
    var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
    var doc = isDocumentFragment ? containerInfo._node: containerInfo._ownerDocument;
    listenTo(registrationName, doc);
    transaction.getReactMountReady().enqueue(putListener, {
        inst: inst,
        registrationName: registrationName,
        listener: listener
    });
}複製代碼

listenTo(registrationName, doc);中,調用了

ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(dependency, topEventMapping[dependency], mountAt);複製代碼


trapBubbledEvent方法中對document方法添加了click,事件作了代理

這裏的註冊事件的回調函數很是重要,

是後面點擊調用處理事件,觸發setState的起點,點擊事件發生先觸發ReactEventListener.dispatchEvent

中間穿插事務transaction,提升setState更新效率

trapBubbledEvent: function (topLevelType, handlerBaseName, element) {
   return EventListener.listen(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
  },複製代碼



listenTo(registrationName, doc);執行完

transaction.getReactMountReady().enqueue(putListener, {
    inst: inst,
    registrationName: registrationName,
    listener: listener
  });複製代碼

在ReactReconcileTransaction對應的事務實例

transaction.reactMountReady的數組中收集,等更新完畢後,觸發對應的close方法時調用


接下來處理'children',這個不屬於dom屬性

更新完屬性接着

this._updateDOMProperties(null, props, transaction);//更新屬性
var lazyTree = DOMLazyTree(el);
this._createInitialChildren(transaction, props, context, lazyTree);
mountImage = lazyTree;複製代碼


_createInitialChildren中處理children複製代碼




開始掛載children,循環children數組


又回到

根據instantiateReactComponent()實例化組件,此次是ReactDOMTextComponent組件實例

var ReactDOMTextComponent = function(text) {
    // TODO: This is really a ReactText (ReactNode), not a ReactElement
    this._currentElement = text;
    this._stringText = '' + text;
    // ReactDOMComponentTree uses these:
    this._hostNode = null;
    this._hostParent = null;

    // Properties
    this._domID = 0;
    this._mountIndex = 0;
    this._closingComment = null;
    this._commentNodes = null;
};複製代碼

mountComponent: function(transaction, hostParent, hostContainerInfo, context) {
    var domID = hostContainerInfo._idCounter++;
    var openingValue = ' react-text: ' + domID + ' ';
    var closingValue = ' /react-text ';
    this._domID = domID;
    this._hostParent = hostParent;
    if (transaction.useCreateElement) {
        var ownerDocument = hostContainerInfo._ownerDocument;
        var openingComment = ownerDocument.createComment(openingValue);
        var closingComment = ownerDocument.createComment(closingValue);
        var lazyTree = DOMLazyTree(ownerDocument.createDocumentFragment());
        DOMLazyTree.queueChild(lazyTree, DOMLazyTree(openingComment));
        if (this._stringText) {
            DOMLazyTree.queueChild(lazyTree, DOMLazyTree(ownerDocument.createTextNode(this._stringText)));
        }
        DOMLazyTree.queueChild(lazyTree, DOMLazyTree(closingComment));
        ReactDOMComponentTree.precacheNode(this, openingComment);
        this._closingComment = closingComment;
        return lazyTree;
    } else {
        var escapedText = escapeTextContentForBrowser(this._stringText);

        if (transaction.renderToStaticMarkup) {
            // Normally we'd wrap this between comment nodes for the reasons stated // above, but since this is a situation where React won't take over
            // (static pages), we can simply return the text as it is.
            return escapedText;
        }

        return '<!--' + openingValue + '-->' + escapedText + '<!--' + closingValue + '-->';
    }
}複製代碼

在mountChildren中,循環children數組,instantiateReactComponent()實例化組件

children


mountChildren: function (nestedChildren, transaction, context) {
      var children = this._reconcilerInstantiateChildren(nestedChildren, transaction, context);
      this._renderedChildren = children;

      var mountImages = [];
      var index = 0;
      for (var name in children) {
        if (children.hasOwnProperty(name)) {
          var child = children[name];
          var selfDebugID = 0;
          var mountImage = ReactReconciler.mountComponent(child, transaction, this, this._hostContainerInfo, context, selfDebugID);
          child._mountIndex = index++;
          mountImages.push(mountImage);
        }
      }
      return mountImages;
    }複製代碼

而後又開始循環children對象調用ReactDOMTextComponent對應實例的mountComponent

ReactDOMTextComponent的mountComponent比較簡單建立document.createTextNode文本節點插入DocumentFragment文檔片斷


處理完以後,將文檔片斷DocumentFragment,循環插入

上面lazyTree的dom節點span



這樣遞歸就完成回到

ReactCompositeComponentWrapper對應的實例HelloWorld

這個時候雖然Dom節點建立完畢,可是尚未插入頁面節點

咱們都知道componentDidMount是在插入頁面後執行的,這裏的實現就是先將componentDidMount收集到事務transaction的reactMountReady中,等到更新掛載到頁面中在調用

這裏如今存儲了兩個,一個是以前添加onClick時收集的


接下來就回到ReactCompositeComponentWrapper對應的TopLevelWrapper


根包裝組件沒有componentDidMount,直接返回markup,如今回到了mountComponentIntoNode,也就是兩個事務要執行的方法,

接着就要將dom節點插入文檔,執行事務的close了


接着插入container頁面節點,完成以後mountComponentIntoNode方法調用結束




根據上面的事務詳細圖可知道,先ReactReconcileTransaction事務中

  • SELECTION_RESTORATION.close 恢復以前獲取焦點元素(好比input聚焦)
  • EVENT_SUPPRESSION.close 開啓事件響應
  • ON_DOM_READY_QUEUEING,調用更新過程當中收集到事務transaction的reactMountReady
  • 中的內容(上面的componentDidMount和事件監聽添加的內容)
componentDidMount就是經過事務保證必定是在dom掛載後執行的

先執行:putListener


接着執行:componentDidMount



  1. 而後回到ReactDefaultBatchingStrategy中
  • FLUSH_BATCHED_UPDATES.close //執行mount-ready處理程序排隊的任何更新(即componentDidUpdate中調用setState更新)
  • RESET_BATCHED_UPDATES.close爲重置isBatchingUpdates.isBatchingUpdates=false標誌更新結束
到此整個掛載過程就結束了

總結一下:

ReactDOM.render()是渲染React組件並插入到DOM中的入口方法,它的執行流程大概爲

  1.  React.createElement(),建立ReactElement對象。他的重要的成員變量有type,key,ref,props。這個過程當中會調用getInitialState(), 初始化state,只在掛載的時候才調用。後面update時再也不調用了。
  2.  instantiateReactComponent(),根據ReactElement的type分別建立ReactDOMComponent, ReactCompositeComponent,ReactDOMTextComponent等對象 mountComponent(), 調用React生命週期方法解析組件,獲得它的HTML。
  3.  _mountImageIntoNode(), 將HTML插入到DOM父節點中,經過設置DOM父節點的innerHTML屬性。
  4.  緩存節點在React中的對應對象,即Virtual DOM。


第二部分:setState更新過程:

感受文章寫的有點長,後面就簡單講解了:

仍是要用到事務:

ReactComponent.prototype.setState = function (partialState, callback) {
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};複製代碼

看到setState調用的是this.updater.enqueueSetState(this, partialState)

根據上面的掛載過程能夠看到this.updater是在ReactCompositeComponent實例調用

mountComponent掛載的


var ReactCompositeComponent = {
    construct: function(element) {
        //...省略
    },
    mountComponent: function(transaction, hostParent, hostContainerInfo, context) {
         //...

        var updateQueue = transaction.getUpdateQueue();
        inst.props = publicProps;
        inst.context = publicContext;
        inst.refs = emptyObject;
        inst.updater = updateQueue;//
       //...
    
    }
}複製代碼

能夠看到 inst.updater的值transaction.getUpdateQueue();

獲得的就是ReactUpdateQueue.js中導出的對象

var ReactUpdateQueue = {
   //....
  enqueueCallback: function (publicInstance, callback, callerName) {
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
    if (!internalInstance) {
      return null;
    }
    if (internalInstance._pendingCallbacks) {
      internalInstance._pendingCallbacks.push(callback);
    } else {
      internalInstance._pendingCallbacks = [callback];
    }
    enqueueUpdate(internalInstance);
  },

  enqueueCallbackInternal: function (internalInstance, callback) {
    if (internalInstance._pendingCallbacks) {
      internalInstance._pendingCallbacks.push(callback);
    } else {
      internalInstance._pendingCallbacks = [callback];
    }
    enqueueUpdate(internalInstance);
  }
//....
}複製代碼


從第一部分掛載中說明的重點註冊onClick開始:

根據第一部分在document上註冊click事件的回調函數是ReactEventListener.dispatchEvent

因此點擊時觸發


這裏開始進入批量更新:ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);

又進入了ReactDefaultBatchingStrategy事務中

var ReactDefaultBatchingStrategy = {
  isBatchingUpdates: false,

  batchedUpdates: function (callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;
    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
      return transaction.perform(callback, null, a, b, c, d, e);
    }
  }
};複製代碼




  • 根據上面的事務詳細圖可知道

    1. ReactDefaultBatchingStrategy中
    • FLUSH_BATCHED_UPDATES.initialize爲空函數
    • RESET_BATCHED_UPDATES.initialize爲空函數
    接着進入要執行的函數handleTopLevelImpl

從發生的原生,沿着冒泡的順序向上找到對應的父組件的隊列,循環調用處理


bookKeeping.ancestors列表結構:


通過一些複雜的前期處理後,調用定義的handleMessage



接着就是調用setSate(),先將傳入的 {className: "list-wrap",message: "哈哈"}放入實例

ReactCompositeComponentWrapper的實例HelloWorld的_pendingStateQueue中


接着調用

function enqueueUpdate(internalInstance) {
  ReactUpdates.enqueueUpdate(internalInstance);
}複製代碼

function enqueueUpdate(component) {
    if (!batchingStrategy.isBatchingUpdates) {
        batchingStrategy.batchedUpdates(enqueueUpdate, component);
        return;
    }

    dirtyComponents.push(component);
    if (component._updateBatchNumber == null) {
        component._updateBatchNumber = updateBatchNumber + 1;
    }
}複製代碼

因爲這是處於事務中batchingStrategy.isBatchingUpdates=true


組件就會放在dirtyComponents,

這就是React高效的更新,處在事務中,不管調用setState多少次都只作一次更新,全部的setState都會放入dirtyComponents中

過程以下圖所示:



這樣處在事務中的handleTopLevelImpl就執行完畢

接下來要執行事務的close方法去更新內容了

  1. 回到ReactDefaultBatchingStrategy中
  • FLUSH_BATCHED_UPDATES.close 

flushBatchedUpdates = function() {
    while (dirtyComponents.length || asapEnqueued) {
        if (dirtyComponents.length) {
            var transaction = ReactUpdatesFlushTransaction.getPooled();
            transaction.perform(runBatchedUpdates, null, transaction);
            ReactUpdatesFlushTransaction.release(transaction);
        }

        if (asapEnqueued) {
            asapEnqueued = false;
            var queue = asapCallbackQueue;
            asapCallbackQueue = CallbackQueue.getPooled();
            queue.notifyAll();
            CallbackQueue.release(queue);
        }
    }
};複製代碼

執行close方法調用flushBatchedUpdates方法,其中又包含了一個事務

var transaction = ReactUpdatesFlushTransaction.getPooled();
    transaction.perform(runBatchedUpdates, null, transaction);複製代碼

這個ReactUpdatesFlushTransaction事務中包含兩塊以下: 

var NESTED_UPDATES = {
    initialize: function() {
       this.dirtyComponentsLength = dirtyComponents.length;
    },
    close: function() {
        if (this.dirtyComponentsLength !== dirtyComponents.length) {
            dirtyComponents.splice(0, this.dirtyComponentsLength);
            flushBatchedUpdates();
        } else {
            dirtyComponents.length = 0;
        }
    }
};

var UPDATE_QUEUEING = {
    initialize: function() {
        this.callbackQueue.reset();
    },
    close: function() {
        this.callbackQueue.notifyAll();
    }
};

var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];複製代碼

下面貼一下事務執行圖




根據上面的事務詳細圖可知道

  1. ReactUpdatesFlushTransaction中
  • NESTED_UPDATES.initialize // 初始化this.dirtyComponentsLength個數
  • UPDATE_QUEUEING.initialize //重置this.callbackQueue,用來存儲須要更新完成的代碼

ReactReconcileTransaction事務中

  • SELECTION_RESTORATION.initialize獲取更新前焦點元素,方便更新後恢復(好比input聚焦)
  • EVENT_SUPPRESSION.initialize,關閉事件響應
  • ON_DOM_READY_QUEUEING,初始化onDOMReady列表







  • RESET_BATCHED_UPDATES.close爲重置isBatchingUpdates.isBatchingUpdates=false標誌更新結束

開始執行runBatchedUpdates

function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  dirtyComponents.sort(mountOrderComparator);

  for (var i = 0; i < len; i++) {
    // dirtyComponents中取出一個component
    var component = dirtyComponents[i];

    // 取出dirtyComponent中的未執行的callback,下面就準備執行它了
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;

    var markerName;
    if (ReactFeatureFlags.logTopLevelRenders) {
      var namedComponent = component;
      if (component._currentElement.props === component._renderedComponent._currentElement) {
        namedComponent = component._renderedComponent;
      }
    }
    // 執行updateComponent
    ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction);

    // 執行dirtyComponent中以前未執行的callback
    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance());
      }
    }
  }
}複製代碼

runBatchedUpdates循環遍歷dirtyComponents數組,主要幹兩件事。首先執行performUpdateIfNecessary來刷新組件的view,而後執行以前阻塞的callback。下面來看performUpdateIfNecessary。

performUpdateIfNecessary: function (transaction) {
    if (this._pendingElement != null) {
      // receiveComponent會最終調用到updateComponent,從而刷新View
      ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
    }

    if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      // 執行updateComponent,從而刷新View。這個流程在React生命週期中講解過
      this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
    }
  }複製代碼


開始調用ReactCompositeComponentWrapper對應的實例HelloWorld的updateComponent


這裏若是有componentWillReceiveProps調用了這個生命週期


合併state變化,統一處理


都知道自定義組件有兩種形式:

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
或者
class Greeting extends React.PureComponent {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}複製代碼

React.PureComponentReact.Component 很類似。二者的區別在於 React.Component並未實現 shouldComponentUpdate(),而 React.PureComponent 中以淺層對比 prop 和 state 的方式來實現了該函數。

React.PureComponent 中的 shouldComponentUpdate() 僅做對象的淺層比較


代碼處理判斷在這裏:


接着往下走:

this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);

//ReactCompositeComponent中

_performComponentUpdate: function(nextElement, nextProps, nextState, nextContext, transaction, unmaskedContext) {
    var _this2 = this;

    var inst = this._instance;

    var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
    var prevProps;
    var prevState;
    var prevContext;
    if (hasComponentDidUpdate) {
        prevProps = inst.props;
        prevState = inst.state;
        prevContext = inst.context;
    }

    if (inst.componentWillUpdate) {
        if (process.env.NODE_ENV !== 'production') {
            measureLifeCyclePerf(function() {
                return inst.componentWillUpdate(nextProps, nextState, nextContext);
            },
            this._debugID, 'componentWillUpdate');
        } else {
            inst.componentWillUpdate(nextProps, nextState, nextContext);
        }
    }

    this._currentElement = nextElement;
    this._context = unmaskedContext;
    inst.props = nextProps;
    inst.state = nextState;
    inst.context = nextContext;

    this._updateRenderedComponent(transaction, unmaskedContext);

    if (hasComponentDidUpdate) {
        if (process.env.NODE_ENV !== 'production') {
            transaction.getReactMountReady().enqueue(function() {
                measureLifeCyclePerf(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), _this2._debugID, 'componentDidUpdate');
            });
        } else {
            transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst);
        }
    }
}複製代碼


調用componentWillUpdate生命週期



接下來進入:

_updateRenderedComponent: function(transaction, context) {
    var prevComponentInstance = this._renderedComponent;
    var prevRenderedElement = prevComponentInstance._currentElement;

    // _renderValidatedComponent內部會調用render,獲得ReactElement
    var nextRenderedElement = this._renderValidatedComponent();

    // 判斷是否作DOM diff。React爲了簡化遞歸diff,認爲組件層級不變,且type和key不變(key用於listView等組件,不少時候咱們沒有設置type)才update,不然先unmount再從新mount
    if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
      // 遞歸updateComponent,更新子組件的Virtual DOM
      ReactReconciler.receiveComponent(prevComponentInstance, nextRenderedElement, transaction, this._processChildContext(context));
    } else {
      var oldNativeNode = ReactReconciler.getNativeNode(prevComponentInstance);

      // 不作DOM diff,則先卸載掉,而後再加載。也就是先unMountComponent,再mountComponent
      ReactReconciler.unmountComponent(prevComponentInstance, false);

      this._renderedNodeType = ReactNodeTypes.getType(nextRenderedElement);

      // 由ReactElement建立ReactComponent
      this._renderedComponent = this._instantiateReactComponent(nextRenderedElement);

      // mountComponent掛載組件,獲得組件對應HTML
      var nextMarkup = ReactReconciler.mountComponent(this._renderedComponent, transaction, this._nativeParent, this._nativeContainerInfo, this._processChildContext(context));

      // 將HTML插入DOM中
      this._replaceNodeWithMarkup(oldNativeNode, nextMarkup, prevComponentInstance);
    }
}複製代碼

再次調用render方法:

經過shouldUpdateReactComponent(prevElement, nextElement)比較

判斷是否作DOM diff

和mountComponent中同樣,updateComponent也是用遞歸的方式將各子組件進行update的。這裏要特別注意的是DOM diff。DOM diff是React中渲染加速的關鍵所在,它會幫咱們算出virtual DOM中真正變化的部分,並對這部分進行原生DOM操做。爲了不循環遞歸對比節點的低效率,React中作了假設,即只對層級不變,type不變,key不變的組件進行Virtual DOM更新


這裏是須要的


總結 setState流程仍是很複雜的,設計也很精巧,避免了重複無謂的刷新組件。它的主要流程以下 :

  1.  enqueueSetState將state放入隊列中,並調用enqueueUpdate處理要更新的Component 
  2. 若是組件當前正處於update事務中,則先將Component存入dirtyComponent中。不然調用batchedUpdates處理。
  3.  batchedUpdates發起一次transaction.perform()事務 
  4. 開始執行事務初始化,運行,結束三個階段 
    1. 初始化:事務初始化階段沒有註冊方法,故無方法要執行 
    2. 運行:執行setSate時傳入的callback方法,通常不會傳callback參數
    3.  結束:更新isBatchingUpdates爲false,並執行FLUSH_BATCHED_UPDATES這個wrapper中的close方法
  5. FLUSH_BATCHED_UPDATES在close階段,會循環遍歷全部的dirtyComponents,調用updateComponent刷新組件,並執行它的pendingCallbacks, 也就是setState中設置的callback。
相關文章
相關標籤/搜索