今天看啥  ›  专栏  ›  litongqian

React源码简析(版本React-15.2.0)

litongqian  · 掘金  ·  · 2019-06-29 03:39
阅读 175

React源码简析(版本React-15.2.0)

前言:

         用了这么久的React,是时候啃一下源码了,本来想直接上React16的源码,发现加入React Fiber后,看起来非常痛苦.没办法只能先啃老的,再不学就跟不上了!

收拾下心情,进入正题之前,先讲一下React的难点事务,React中存在大量事务的使用

1. transaction事务

先看一下事务的代码实现:

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;
    }
};复制代码

举个例子:

function update(){
    console.log('更新了')
};
Transaction.perform(update);
//会在执行update方法前,先执行initializeAll,循环调用initialize方法
//输出'更新了'
//执行closeAll,循环调用transactionWrappers中的close方法复制代码

这个事务只是抽象了功能getTransactionWrappers为null,具体的执行内容要求子类实现:

也就是说在方法执行前后分别做一些事情!

下面我们看下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 叠加。

下面开始进入正题:

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 !== stringReact组件创建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。

到这里先暂停一下,写不下去了,下次再来整理:
如果你看到这里,有点帮助欢迎点赞,打赏,感谢:








原文地址:访问原文地址
快照地址: 访问文章快照