上回说到了
ReactMount._renderNewRootComponent
中调用了ReactUpdates.batchedUpdates
方法,实质上调用的是ReactDefaultBatchingStrategy.batchedUpdates
,然后就讲了一下Transaction
。那么现在回到流程上来,继续往下走,现在Transaction也了解了,那么剩下的就好办了。
回到流程来,我们来看一下ReactDefaultBatchingStrategy.batchedUpdates
,首先先说一下参数
callback: batchedMountComponentIntoNode // 是在ReactMount文件里
a: componentInstance // 执行instantiateComponent得到的结果,那么当前得到的实例是ReactCompositeComponent
b: container
c: shouleReuseMarkup // false
d: context // {}
e: null
// ReactDefaultBatchingStrategy.js
batchedUpdates: function (callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// The code is written this way to avoid extra allocations
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
return transaction.perform(callback, null, a, b, c, d, e);
}
}
首先拿到当前的isBatchingUpdates
,那么肯定是 false 了啊
然后设置为 true
那么判断isBatchingUpdates
赋值的时候是 false,所以执行transaction.perform
,那么这边的transaction
指的是ReactDefaultBatchingStrategyTransaction
// ReactDefaultBatchingStrategy.js
var transaction = new ReactDefaultBatchingStrategyTransaction();
那么这边执行perform
,根据上一篇博客讲的,会首先执行所有wrapper的initialize方法。
perform: function (method, scope, a, b, c, d, e, f) {
!!this.isInTransaction() ? /**/
var errorThrown;
var ret;
try {
this._isInTransaction = true;
errorThrown = true;
this.initializeAll(0);
ret = method.call(scope, a, b, c, d, e, f);
errorThrown = false;
} finally {
try {
if (errorThrown) {
try {
this.closeAll(0);
} catch (err) {}
} else {
this.closeAll(0);
}
} finally {
this._isInTransaction = false;
}
}
return ret;
}
但是ReactDefaultBatchingStrategyTransaction
包裹的wrapper
都是空函数。
那就到了这一步ret = method.call(scope, a, b, c, d, e, f);
开始执行我们传递的这个方法。也就是执行ReactMout
里的batchedMountComponentIntoNode
// ReactMount.js
function batchedMountComponentIntoNode(componentInstance, container, shouldReuseMarkup, context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
!shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement);
transaction.perform(mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
首先从ReactUpdates.ReactReconcileTransaction.getPooled(true)
拿到池子里的一个ReactReconcileTransaction
实例。ReactUpdates
和getPooled
在之前的博客已经做过声明了,需要的同学可以自行查看。
然后执行perform
方法,这边执行的是ReactReconcileTransaction.perform
。
最后释放这个transaction
那么关键的地方在于这边又执行了一个Transction(ReactReconcileTransaction)。
一样的我们先记录一下传递的参数。
callback: mountComponentIntoNode // ReactMount.js里的
scope: null
a: componentInstance
b: container
c: transaction // ReactReconcileTransaction
d: shouldReuseMarkup // false
e: context // {}
那么先执行所有的initialize
// ReactReconcileTransaction.js
var SELECTION_RESTORATION = {
/**
* @return {Selection} Selection information.
*/
initialize: ReactInputSelection.getSelectionInformation,
/**/
};
var EVENT_SUPPRESSION = {
initialize: function () {
var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
ReactBrowserEventEmitter.setEnabled(false);
return currentlyEnabled;
},
}
var ON_DOM_READY_QUEUEING = {
initialize: function () {
this.reactMountReady.reset();
}
};
第三个wrapper的初始化执行this.reactMountReady.reset()
实质上就是执行callbackQueue.reset
,这个函数很简单就是将callbackQueue的_callbacks
_contexts
都清空设为null
CallbackQueue.prototype.reset = function reset() {
this._callbacks = null;
this._contexts = null;
};
之后就是执行包装的这个函数mountComponentIntoNode
// ReactMount.js
function mountComponentIntoNode(wrapperInstance, container, transaction, shouldReuseMarkup, context) {
var markerName;
// false
if (ReactFeatureFlags.logTopLevelRenders) {
var wrappedElement = wrapperInstance._currentElement.props.child;
var type = wrappedElement.type;
markerName = 'React mount: ' + (typeof type === 'string' ? type : type.displayName || type.name);
console.time(markerName);
}
var markup = ReactReconciler.mountComponent(wrapperInstance, transaction, null, ReactDOMContainerInfo(wrapperInstance, container), context, 0 /* parentDebugID */
);
if (markerName) {
console.timeEnd(markerName);
}
wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;
ReactMount._mountImageIntoNode(markup, container, wrapperInstance, shouldReuseMarkup, transaction);
}
判断ReactFeatureFlags.logTopLevelRenders
,当前值为false
,接下来执行ReactReconciler.mountComponent
,这个方法也是一个重点。拿到markup
后,会执行ReactMount._mountImageIntoNode
,这个函数开始将dom挂载在container上。从这就开始有点挂载的样子了。
这边可以看到参数中调用了一个ReactDOMContainerInfo(wrapperInstance, container)
,这个函数主要的作用就是封装了一下节点的信息,例如节点类型,节点的namespaceURI之类的。那么在函数调用时,首先是先调用这个函数拿到他的返回值之后作为形参然后调用原函数。
// ReactDOMContainerInfo.js
function ReactDOMContainerInfo(topLevelWrapper, node) {
var info = {
_topLevelWrapper: topLevelWrapper,
_idCounter: 1,
_ownerDocument: node ? node.nodeType === DOC_NODE_TYPE ? node : node.ownerDocument : null,
_node: node,
_tag: node ? node.nodeName.toLowerCase() : null,
_namespaceURI: node ? node.namespaceURI : null
};
if (process.env.NODE_ENV !== 'production') {
/**/
}
return info;
}
上面这个返回的info
就是封装的节点信息对象。
先记录一下参数
internalInstance:wrapperInstance,这边的wrapperInstance还是上一个函数的形参,所以需要用流程图好好的记录一下参数的传递。
transaction: ReconcileTransaction
hostParent: null
hostContainerInfo: ReactDOMContainerInfo(wrapperInstance, container)
context: {}
parentDebugId: 0
// ReactReconcile.js
mountComponent: function (internalInstance, transaction, hostParent, hostContainerInfo, context, parentDebugID) // 0 in production and for roots
{
if (process.env.NODE_ENV !== 'production') {
/**/
}
var markup = internalInstance.mountComponent(transaction, hostParent, hostContainerInfo, context, parentDebugID);
if (internalInstance._currentElement && internalInstance._currentElement.ref != null) {
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
}
if (process.env.NODE_ENV !== 'production') {
/**/
}
return markup;
},
内部首先执行实例的mountComponent
,当前传入的实例是wrapperInstance
,那么这个实例是ReactCompositeComponentWrapper
的一个实例。所以这边转到ReactCompositeComponent.js
文件
ReactCompositeComponent
在讲解ReactCompositeComponent.mountComponent
之前,先看一下React15中的生命周期。React16的生命周期和15是不一样的话,废除了几个换成了几个新的,这边就不涉及了。
那么这张图写的就很清楚了
在挂载阶段
- 根组件的
constructor
- 根组件的
componentWillMount
- 根组件的
render
- 子组件的
constructor
- 子组件的
componentWillMount
- 子组件的
render
- 子组件的
componentDidMount
- 子组件的
- 根组件的
componentDidMount
更新阶段 同 挂在阶段
卸载阶段
- 根组件的
componentWillUnMount
- 子组件的
componentWillUnMount
- 子组件销毁
- 根组件销毁
那么按照我们的例子就是
-
TopLevelWrapper
的constructor
-
TopLevelWrapper
的componentWillMount
-
TopLevelWrapper
的render
-
App
的constructor
-
App
的componentWillMount
-
App
的render
-
App
的componentDidMount
-
-
TopLevelWrapper
的componentDidMount
mountComponent
// ReactCompositeComponent.js
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;
// Support functional components
if (!doConstruct && (inst == null || inst.render == null)) {
renderedElement = inst;
warnIfInvalidElement(Component, renderedElement);
!(inst === null || inst === false || React.isValidElement(inst)) ? process.env.NODE_ENV !== 'production' ? /**/
inst = new StatelessComponent(Component);
this._compositeType = CompositeTypes.StatelessFunctional;
} else {
if (isPureComponent(Component)) {
this._compositeType = CompositeTypes.PureClass;
} else {
this._compositeType = CompositeTypes.ImpureClass;
}
}
if (process.env.NODE_ENV !== 'production') {
// This will throw later in _renderValidatedComponent, but add an early
// warning now to help debugging
if (inst.render == null) {
process.env.NODE_ENV !== 'production' ? /**/
}
var propsMutated = inst.props !== publicProps;
var componentName = Component.displayName || Component.name || 'Component';
process.env.NODE_ENV !== 'production' ? /**/
}
// These should be set up in the constructor, but as a convenience for
// simpler class abstractions, we set them up after the fact.
// 这些应该在构造函数中设置,但是为了方便更简单的类抽象,我们在事后才设置它们。
inst.props = publicProps;
inst.context = publicContext;
inst.refs = emptyObject;
inst.updater = updateQueue;
this._instance = inst;
// Store a reference from the instance back to the internal representation
ReactInstanceMap.set(inst, this);
if (process.env.NODE_ENV !== 'production') {
/**/
}
var initialState = inst.state;
if (initialState === undefined) {
inst.state = initialState = null;
}
!(typeof initialState === 'object' && !Array.isArray(initialState)) ? /**/
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
var markup;
if (inst.unstable_handleError) {
markup = this.performInitialMountWithErrorHandling(renderedElement, hostParent, hostContainerInfo, transaction, context);
} else {
markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
}
if (inst.componentDidMount) {
if (process.env.NODE_ENV !== 'production') {
transaction.getReactMountReady().enqueue(function () {
measureLifeCyclePerf(function () {
return inst.componentDidMount();
}, _this._debugID, 'componentDidMount');
});
} else {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
}
}
return markup;
},
代码有点长,那么来一块一块的看。
那么首先是给当前实例添加一些属性
this._context = context;
this._mountOrder = nextMountID++;
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
这边呢有一个nextMountID
变量,这是外部闭包的一个变量,用于记录着挂载的顺序的。
/**
* An incrementing ID assigned to each when it is mounted. This is
* used to enforce the order in which `ReactUpdates` updates dirty components.
* 这用于强制执行“ReactUpdates”更新脏组件的顺序。
* @private
*/
var nextMountID = 1;
再往下
var updateQueue = transaction.getUpdateQueue();
这边获取了当前事务的ReactUpdateQueue
实例。
// Initialize the public class
var Component = this._currentElement.type;
var doConstruct = shouldConstruct(Component); // true
这边调用shouldConstruct
函数对this._currentElement.type
做一个判断,判断原型链中是否是isReactComponent
属性。而当前的_currentElement
是一开始的nextWrapperElement
他是对TopLevelWrapper
执行ReactElement
方法得到的ReactElement。那么当前的type
就是这边的TopLevelWrapper
。那么找ReactMount.js
发现,TopLevelWrapper
的原型链上是有isReactComponent
属性的。
shouldConstruct
// ReactCompositeComponent.js
function shouldConstruct(Component) {
return !!(Component.prototype && Component.prototype.isReactComponent);
}
// ReactMount.js
var TopLevelWrapper = function () {
this.rootID = topLevelRootCounter++;
};
TopLevelWrapper.prototype.isReactComponent = {};
接下来执行this._constructComponent
方法。传递参数为
doConstruct: true
publicProps: this._currentElement.props // 当前为nextWrapperElement的props,存储的是type为App构造函数的ReactElement
publicContext: this._processContext(context) // {}
updateQueue: transaction.getUpdateQueue() // ReactUpdateQueue实例
_constructComponent
_constructComponent: function (doConstruct, publicProps, publicContext, updateQueue) {
if (process.env.NODE_ENV !== 'production' && !doConstruct) {
/**/
} else {
return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue);
}
},
本质上是执行的this._constructComponentWithoutOwner
,参数还是原封不动的传递过去
_constructComponentWithoutOwner
_constructComponentWithoutOwner: function (doConstruct, publicProps, publicContext, updateQueue) {
var Component = this._currentElement.type;
if (doConstruct) {
if (process.env.NODE_ENV !== 'production') {
/**/
} else {
return new Component(publicProps, publicContext, updateQueue);
}
}
// This can still be an instance in case of factory components
// but we'll count this as time spent rendering as the more common case.
if (process.env.NODE_ENV !== 'production') {
/**/
} else {
return Component(publicProps, publicContext, updateQueue);
}
},
如果这边的doConstruct
是true
的话,那么就会返回一个this._currentElement.type
的实例,否则的话则会调用this._currentElement.type
返回其返回值。这边笼统的说就是要实例化一个this._currentElement.type
存储的值。
那么当前doConstruct
是为true
的,也就是说会返回一个TopLevelWrapper
实例。
那么回到mountComponent方法
中继续往下走
var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
这边的inst
现在就是TopLevelWrapper
实例了。继续往下走
// Support functional components
if (!doConstruct && (inst == null || inst.render == null)) {
renderedElement = inst;
warnIfInvalidElement(Component, renderedElement);
!(inst === null || inst === false || React.isValidElement(inst)) ? process.env.NODE_ENV !== 'production' ? /**/
inst = new StatelessComponent(Component);
this._compositeType = CompositeTypes.StatelessFunctional;
} else {
if (isPureComponent(Component)) {
this._compositeType = CompositeTypes.PureClass; // 1
} else {
this._compositeType = CompositeTypes.ImpureClass; // 0
}
}
第一个if
根据注释猜测是函数式组件才会调用。那么当前的情况是进入到了else
内部。主要操作在于给当前实例添加_compositeType
对象。值是根据isPureComponent
函数来决定的。
这边的CompositeTypes
是外部闭包的一个对象,其实就是一个枚举。
var CompositeTypes = {
ImpureClass: 0, // 非 PureClass
PureClass: 1, // PureClass
StatelessFunctional: 2 // 无状态的函数式组件
};
那么总的来说就是如果我们这个Component
是PureClass
的话,_compositeType
对应CompositeTypes.ImpureClass
,否则的话就是CompositeTypes.PureClass
。在第一篇博客讲解React的全局API时,对于PureComponent
是有一个特殊的标志的isPureReactComponent
。
// ReactCompositeComponent.js
function isPureComponent(Component) {
return !!(Component.prototype && Component.prototype.isPureReactComponent);
}
// react/lib/ReactBaseClass.js
function ReactPureComponent(props, context, updater) {
// Duplicated from ReactComponent.
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
ReactPureComponent.prototype.isPureReactComponent = true;
那么接着往下
// These should be set up in the constructor, but as a convenience for
// simpler class abstractions, we set them up after the fact.
// 这些应该在构造函数中设置,但是为了方便更简单的类抽象,我们在事后才设置它们。
inst.props = publicProps;
inst.context = publicContext;
inst.refs = emptyObject;
inst.updater = updateQueue;
给inst
附加属性,官方注释的解释是这些属性本应在构造函数中设置但是为了更简单的类抽象,才在这边进行附加。
this._instance = inst;
给当前实例附加_instance
属性,值为inst
// Store a reference from the instance back to the internal representation
ReactInstanceMap.set(inst, this);
将当前的inst
存储起来。ReactInstanceMap
对象做的事情就是一件事,维护来自面向公共的有状态实例(键)和内部表示(值)的映射。这允许公共方法接受面向用户的实例作为参数,并将它们映射回内部方法。
说白了给key值附加一个属性_reactInternalInstance
,值为传入的value。
那么这边就是给inst
附加一个_reactInternalInstance
属性,值为this
也就是当前ReactCompositeComponent
实例。
var initialState = inst.state;
if (initialState === undefined) {
inst.state = initialState = null;
}
获取初始的state
,不存在的话初始化为null
走这开始就可以把生命周期给连上了。这一步对应着我们在构造函数里写的this.state = {}
,例子如下
class App extends React.Component {
constructor(props) {
super(props);
// 对应着这里
this.state = {
name: 'Hello World'
}
}
/**/
}
但是我们当前的inst
是TopLevelWrapper
显然没有这么个东西。
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
附加几个属性,这几个属性用在更新的时候。
var markup;
if (inst.unstable_handleError) {
markup = this.performInitialMountWithErrorHandling(renderedElement, hostParent, hostContainerInfo, transaction, context);
} else {
markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
}
这边对inst.unstable_handleError
判断之后执行初始化的挂载,那么这边显然没有这个属性。这个performInitialMount
方法就对应着我们的componentWillMount
生命周期。
performInitialMount
performInitialMount: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
var inst = this._instance;
var debugID = 0;
if (process.env.NODE_ENV !== 'production') {
debugID = this._debugID;
}
if (inst.componentWillMount) {
if (process.env.NODE_ENV !== 'production') {
/**/
} else {
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 not a stateless component, we now render
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);
if (process.env.NODE_ENV !== 'production') {
/**/
}
return markup;
},
那么上面也提到了这个函数相当于componentWillMount
,执行初始化的挂载。
if (inst.componentWillMount) {
if (process.env.NODE_ENV !== 'production') {
measureLifeCyclePerf(function () {
return inst.componentWillMount();
}, debugID, 'componentWillMount');
} else {
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);
}
}
那么这边判断componentWillMount
是否存在,存在的话就执行,在执行期间执行的setState
将会加入到_pendingStateQueue
,这边需要注意的是,在componentWillMount
期间执行的setState
并不会引发重新的render
。
执行完之后判断_pendingStateQueue
是否存在,也就是判断在执行期间是否调用了setState
,如果有的话会将这个队列中的state进行比较,得到一个最终的state。
_processPendingState
_processPendingState: function (props, context) {
var inst = this._instance;
var queue = this._pendingStateQueue;
var replace = this._pendingReplaceState;
this._pendingReplaceState = false;
this._pendingStateQueue = null;
if (!queue) {
return inst.state;
}
if (replace && queue.length === 1) {
return queue[0];
}
var nextState = _assign({}, replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
_assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
}
return nextState;
}
通过Object.assign
一个一个的覆盖前者得到一个最终的state。得到最终的一个state之后赋值给inst.state
,之后就是判断传入的这个renderedElement
是否是undefined
了。
// If not a stateless component, we now render
if (renderedElement === undefined) {
renderedElement = this._renderValidatedComponent();
}
根据这个英文注释了解到如果是一个无状态组件组件,那么就渲染他,突然发现我们的TopLevelWrapper
就是一个无状态的组件,那么当前就是undefined
,ok,渲染他。
_renderValidatedComponent
_renderValidatedComponent: function () {
var renderedElement;
if (process.env.NODE_ENV !== 'production' || this._compositeType !== CompositeTypes.StatelessFunctional) {
ReactCurrentOwner.current = this;
try {
renderedElement = this._renderValidatedComponentWithoutOwnerOrContext();
} finally {
ReactCurrentOwner.current = null;
}
} else {
renderedElement = this._renderValidatedComponentWithoutOwnerOrContext();
}
!(
// TODO: An `isValidNode` function would probably be more appropriate
renderedElement === null || renderedElement === false || React.isValidElement(renderedElement)) ? process.env.NODE_ENV !== 'production' ? /**/
return renderedElement;
},
那么实际上就是执行_renderValidatedComponentWithoutOwnerOrContext
函数并将返回值返回出来。
_renderValidatedComponentWithoutOwnerOrContext
_renderValidatedComponentWithoutOwnerOrContext: function () {
var inst = this._instance;
var renderedElement;
if (process.env.NODE_ENV !== 'production') {
renderedElement = measureLifeCyclePerf(function () {
return inst.render();
}, this._debugID, 'render');
} else {
renderedElement = inst.render();
}
if (process.env.NODE_ENV !== 'production') {
// We allow auto-mocks to proceed as if they're returning null.
if (renderedElement === undefined && inst.render._isMockFunction) {
// This is probably bad practice. Consider warning here and
// deprecating this convenience.
renderedElement = null;
}
}
return renderedElement;
},
这个就简单了,直接执行this._instance.render
,那么当前的_instance
是TopLevelWrapper
实例,他的render方法就是返回他的props.child
// ReactMount.js
TopLevelWrapper.prototype.render = function () {
return this.props.child;
};
那么这边的child
就是我们的React.createElement(App)
的结果。将返回值依次返回那么就回到了performInitialMount
函数。
// ReactCompositeComponent.performInitialMount
// If not a stateless component, we now render
if (renderedElement === undefined) {
renderedElement = this._renderValidatedComponent();
}
也就是回到了这,如果有童鞋打断点的话,那么可以看
Chrome 里面的Call Stack
看整个流程的函数调用栈。
那么继续往下走
var nodeType = ReactNodeTypes.getType(renderedElement);
this._renderedNodeType = nodeType;
这边给当前实例附加一个_renderedNodeType
属性,调用了一个ReactNodeTypes.getType()
方法,
// ReactNodeType.js
var ReactNodeTypes = {
HOST: 0,
COMPOSITE: 1,
EMPTY: 2,
getType: function (node) {
if (node === null || node === false) {
return ReactNodeTypes.EMPTY;
} else if (React.isValidElement(node)) {
if (typeof node.type === 'function') {
return ReactNodeTypes.COMPOSITE;
} else {
return ReactNodeTypes.HOST;
}
}
!false ? process.env.NODE_ENV !== 'production' ? /**/
}
};
根据node
型和node.type
进行判断,主要目的就是判断当前的node
是哪一种类型的组件,这个操作在instantiateReactComponent
中有类似的操作。继续往下
var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY);
调用_instantiateReactComponent
函数,该函数是在实例化当前实例时进行赋值的也就是instantiateReactComponent.js
里面的方法,重要目的就是创建一个ReactComponent实例。
具体的操作就不去看了之前已经介绍过,那么我们需要注意的是这边传入的renderedElement
参数是我们的类型为App
的ReactElement
,那么根据之前的介绍,这个方法会返回一个ReactCompositeComponent
的实例。
那么继续往下
this._renderedComponent = child;
附加_renderedComponent
属性值为上一步得到的child
继续往下
var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID);
执行ReactReconcile.mountComponent
,那么又回到了ReactCompositeComponent.mountComponent
函数,只不过this
指向了这个child
那么我们来看一下当前的调用栈
再来看一下this
指向
可以看到this._currentElement
是type为App的ReactElement。
那么还是一样的附加几个属性,这边的_mountOrder
就是2
了
this._mountOrder = nextMountID++;
再往下经过一系列平淡无奇的操作之后到了这
var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
这边需要注意的是实例化App时,传入了这个updateQueue
,而App的构造函数内部调用了super(props)
调用了一次父类的构造函数也就是React.Component
的构造函数,
// ReactBaseClasses.js
function ReactComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
那么这边只传递了props
,其余两个变量都是undefined
,那么重点在这
this.updater = updater || ReactNoopUpdateQueue;
这边的this.updater
赋值的是ReactNoopUpdateQueue
对象。
那么我们来看一下这个App的实例有哪些属性。
原型链上的几个方法是通过babel打进去的。
在往下又经历了一系列的操作到了这
inst.props = publicProps;
inst.context = publicContext;
inst.refs = emptyObject;
inst.updater = updateQueue;
重点在于最后一句,对inst.updater
重新赋值了,一开始实例化App的时候用的是缺省的ReactNoopUpdateQueue
,那么这边是重新改成ReactUpdateQueue
,我估计是防止有人在执行super()
的时候,传了不止props
一个参数,这边做一个重新赋值保证updater
一定是ReactUpdateQueue
.
再次经历一系列的操作之后进入了performInitialMount
函数,开始执行componentWillMount
方法,但是我们的componentWillMount
方法里调用了setState
,我们来看一下React对setState
的处理。
ReactComponent.prototype.setState = function (partialState, callback) {
!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? /**/
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
调用this.updater.enqueueSetState
方法,那么我们这边的updater
上面也说了是一个ReactUpdateQueue
,来看一下这个对象里的方法。
//ReactUpdateQueue.js
enqueueSetState: function (publicInstance, partialState) {
if (process.env.NODE_ENV !== 'production') {
/**/
}
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
if (!internalInstance) {
return;
}
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
enqueueUpdate(internalInstance);
},
那么这边调用getInternalInstanceReadyForUpdate
方法从ReactInstanceMap
�中查找对应的key值,主要就是查找对应的实例是否存在,不存在的话就报错退出。
之后就是将新的state加入到实例的_pendingStateQueue
里面,上文也说了执行完componentWillMount
之后会检查_pendingStateQueue
得到最新的state之后对实例的state进行赋值。
之后调用enqueueUpdate
方法
// ReactUpdateQueue.js
function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
实际上就是执行ReactUpdates.enqueueUpdate
,ok,我们跳到这里看一下发生了什么。
function enqueueUpdate(component) {
ensureInjected();
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case. (This is called by each top-level update
// function, like setState, forceUpdate, etc.; creation and
// destruction of top-level components is guarded in ReactMount.)
// 我们代码的各个部分(比如ReactCompositeComponent的_renderValidatedComponent)
// 假设render调用不是嵌套的;验证一下。(这是由每个顶级更新函数调用,如setState、forceUpdate等;
// 顶级组件的创建和销毁在ReactMount中受到保护。)
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
首先确保依赖注入,之后判断batchingStrategy.isBatchingUpdates
变量,那么当前值还是true的。所以下面就将该组件push进dirtyComponents
,并且给组件添加一个_updateBatchNumber
值,这个updateBatchNumber
是外部闭包的一个变量。
一路return回到了performInitialMount
函数,开始检测_pendingStateQueue
,这边按照我们的示例是有一个的,那么就计算出最新的state,那么可以发现React是进行批处理的。
之后因为renderedElement
是undefined
开始执行render
,就到了_renderValidatedComponent
函数,继而又到了_renderValidatedComponentWithoutOwnerOrContext
函数,执行实例的render
方法,我们来看一下App
的render方法
render() {
console.log('render');
return (
<div>
{ this.state.name }
</div>
)
}
render
函数打印一句话然后返回一个DOM节点。然后一路返回到了performInitialMount
方法。
一路平淡无奇到了这
var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY);
那么这会的renderedElement
就是我们render返回出来的那个DOM节点,只不过被babel调用了React.createElement解析成了一个ReactElement。我们来看一下当前的这个ReactElement
那么这边调用_instantiateReactComponent
实际上也就是instantiateReactComponent
方法,这边因为node.type
是一个字符串,那么会调用ReactHostComponent.createInternalComponent
创建一个ReactHostComponent
实例,实际上也就是ReactDOMComponent
实例。
拿到了child
之后,又开始对child
执行挂载,这会执行的就是ReactDOMComponent.mountComponent
。
一系列操作完之后,回到了ReactCompositeComponent.mountComponnet
中继续往下执行。到了这也就是App以下的都挂载完了,开始执行App的componentDidMount
if (inst.componentDidMount) {
if (process.env.NODE_ENV !== 'production') {
/**/
} else {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
}
}
这边直接是把componentDidMount
方法压入ReactReconcileTransaction的callbackQueue队列中。因为这边transaction.getReactMountReady()
是返回的一个callbackQueue。
到此App的componentDidMount
之前的都已经执行完了,回到了ReactReconcile.mountComponent
.
mountComponent: function (internalInstance, transaction, hostParent, hostContainerInfo, context, parentDebugID) // 0 in production and for roots
{
if (process.env.NODE_ENV !== 'production') {
/**/
}
// 走这开始调用实例的挂载
var markup = internalInstance.mountComponent(transaction, hostParent, hostContainerInfo, context, parentDebugID);
if (internalInstance._currentElement && internalInstance._currentElement.ref != null) {
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
}
if (process.env.NODE_ENV !== 'production') {
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onMountComponent(internalInstance._debugID);
}
}
return markup;
}
那么到现在我们可以理会到了ReactReconcile
的作用了,他内部的mountComponent
是所有实例挂载的入口,在ReactReconcile
内部才会开始调用实例自己的mountComponent
。而Reconcile
调度也就是这个意思。
那么再执行完实例自己的mountComponent
之后,判断实例是否具有ref
属性,如果有的话会将一个函数名为attachRefs
,调用上下文为当前传入的实例(internalInstance)压入到事务的callbackQueue中。那么之所以要将函数和上下文都一起保存是因为是要确保回调函数的内部this指向。
那么之后就是一路的return,回到了mountComponentIntoNode
,开始执行ReactMount._mountImageIntoNode
方法
_mountImageIntoNode: function (markup, container, instance, shouldReuseMarkup, transaction) {
/**/
if (shouldReuseMarkup) {
var rootElement = getReactRootElementInContainer(container);
if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
ReactDOMComponentTree.precacheNode(instance, rootElement);
return;
} else {
var checksum = rootElement.getAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
var rootMarkup = rootElement.outerHTML;
rootElement.setAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME, checksum);
var normalizedMarkup = markup;
if (process.env.NODE_ENV !== 'production') {
/**/
}
var diffIndex = firstDifferenceIndex(normalizedMarkup, rootMarkup);
var difference = ' (client) ' + normalizedMarkup.substring(diffIndex - 20, diffIndex + 20) + '\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20);
/**/
}
/**/
if (transaction.useCreateElement) {
// 清空容器子节点,换行符也算一个childNode,lastChild,firstChild是取的childNodes,不是children,换行符也算一个childNode
while (container.lastChild) {
container.removeChild(container.lastChild);
}
DOMLazyTree.insertTreeBefore(container, markup, null);
} else {
setInnerHTML(container, markup);
ReactDOMComponentTree.precacheNode(instance, container.firstChild);
}
if (process.env.NODE_ENV !== 'production') {
/**/
}
}
首先对shouleReuseMarkup
做判断,当前为false
,往下就是判断transaction.useCreateElement
,那么这个useCreateElement
是在实例化ReactReconcileTransaction
时,传给构造函数的一个参数,当前为true
。ok,进入这个if内部
一个while清空container
内部的所有子节点
打断点走到这可以看出root
节点里的子节点都被清空了,然后开始执行DOMLazyTree.insertTreeBefore
方法,开始将虚拟DOM插入到真实DOM中。
var insertTreeBefore = createMicrosoftUnsafeLocalFunction(function (parentNode, tree, referenceNode) {
// DocumentFragments aren't actually part of the DOM after insertion so
// appending children won't update the DOM. We need to ensure the fragment
// is properly populated first, breaking out of our lazy approach for just
// this level. Also, some <object> plugins (like Flash Player) will read
// <param> nodes immediately upon insertion into the DOM, so <object>
// must also be populated prior to insertion into the DOM.
// 文档片段在插入之后实际上不是DOM的一部分,所以附加的子元素不会更新DOM。
// 我们需要确保首先正确填充了片段,从而打破只针对这个级别的惰性方法。
// 此外,一些<object>插件(如Flash Player)在插入到DOM时将立即读取<param>节点,因此在插入到DOM之前还必须填充<object>。
if (tree.node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE || tree.node.nodeType === ELEMENT_NODE_TYPE && tree.node.nodeName.toLowerCase() === 'object' && (tree.node.namespaceURI == null || tree.node.namespaceURI === DOMNamespaces.html)) {
insertTreeChildren(tree);
parentNode.insertBefore(tree.node, referenceNode);
} else {
parentNode.insertBefore(tree.node, referenceNode);
insertTreeChildren(tree);
}
});
对tree.node.nodeType
做判断,当前进入else代码块,只看代码的话会发现if和else的代码块只是顺序不一样。那么接下来就是DOM操作了,将tree.node
插入到parent
的子节点中,这边的referenceNode
是null
,表明在parentNode
的子节点末尾插。
那么这个时候我们发现这个时候节点已经插进去了
接下来调用`insertTreeChildren方法
function insertTreeChildren(tree) {
if (!enableLazy) {
return;
}
var node = tree.node;
var children = tree.children;
if (children.length) {
for (var i = 0; i < children.length; i++) {
insertTreeBefore(node, children[i], null);
}
} else if (tree.html != null) {
setInnerHTML(node, tree.html);
} else if (tree.text != null) {
setTextContent(node, tree.text);
}
}
这边用到了一个变量enableLazy
,这是根据不同的浏览器来判定的。理由在源码的英文注释中给出了
In IE (8-11) and Edge, appending nodes with no children is dramatically
faster than appending a full subtree, so we essentially queue up the
.appendChild calls here and apply them so each node is added to its parent
before any children are added.
In other browsers, doing so is slower or neutral compared to the other order
(in Firefox, twice as slow) so we only do this inversion in IE.
大致意思就是在IE浏览器和Edge浏览器(Microsoft Edge)中,添加没有子节点的节点比直接添加完整的DOM树要快得多。
在其他浏览器中,这样做比另一种顺序(在Firefox中,是两倍慢)更慢或更中立,所以我们只在IE中做这种倒置。
然后就对浏览器做了一个判断
var enableLazy = typeof document !== 'undefined' && typeof document.documentMode === 'number' || typeof navigator !== 'undefined' && typeof navigator.userAgent === 'string' && /\bEdge\/\d/.test(navigator.userAgent);
如果是IE的话那么enableLazy
就是true,那么当前是用的Chrome。所以这个方法就直接return了。这一块做的就是对IE浏览器做不同的插入操作。IE真的是前端的。。。。。
那么到这整个ReactReconcileTransaction.perform
里的callback
就都执行完了,虚拟节点已经转换为真实的DOM节点完成了挂载,但是我们还没有执行componentDidMount
生命周期方法。
之前也看到这个方法是添加到了事务的callbackQueue队列中了,事实上,这个生命周期会在事务关闭的时候被执行,别忘了�事务在关闭的时候会执行所有的close
的。
整个流程已经走到了这。下面就开始执行closeAll
方法执行外层包裹的wrapper的close方法了。
// ReactReconcileTransaction.js
var SELECTION_RESTORATION = {
/**/
/**
* @param {Selection} sel Selection information returned from `initialize`.
*/
close: ReactInputSelection.restoreSelection
};
var EVENT_SUPPRESSION = {
/**/
/**
* @param {boolean} previouslyEnabled Enabled status of
* `ReactBrowserEventEmitter` before the reconciliation occurred. `close`
* restores the previous value.
*/
close: function (previouslyEnabled) {
ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
}
};
var ON_DOM_READY_QUEUEING = {
/**/
/**
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
*/
close: function () {
this.reactMountReady.notifyAll();
}
};
那么前两个close我们就不关注了,来看第三个close
,执行reactMountReady.notifyAll()
方法。
那么这个reactMountReady
本质上是一个callbackQueue
,而notifyAll
方法就是来执行收集到了所有的回调,并通过call
方法将回调函数的this指向到对应的context
。
CallbackQueue.prototype.notifyAll = function notifyAll() {
var callbacks = this._callbacks;
var contexts = this._contexts;
var arg = this._arg;
if (callbacks && contexts) {
!(callbacks.length === contexts.length) ? process.env.NODE_ENV !== 'production' ? /**/
this._callbacks = null;
this._contexts = null;
for (var i = 0; i < callbacks.length; i++) {
callbacks[i].call(contexts[i], arg);
}
callbacks.length = 0;
contexts.length = 0;
}
};
那么目前收集到的回调只有一个componentDidMount
,我们的示例中,在这个生命周期函数中打印了一句话调用了一次setState
,那么根据上面说的setState
会将组件标记为dirtyComponent
并将新的state添加进实例的_pendingStateQueue
。
调用完所有收集到的回调之后就清空_callback
, _context
两个数组。
执行完ReactReconcileTransaction
所有的close之后,将wrapperInitData
清空,在事务的最后也就是finally
代码快,将事务正在执行事务的标记变量记为false
。
finally {
try {
if (errorThrown) {
// If `method` throws, prefer to show that stack trace over any thrown
// by invoking `closeAll`.
try {
this.closeAll(0);
} catch (err) {}
} else {
// Since `method` didn't throw, we don't want to silence the exception
// here.
this.closeAll(0);
}
} finally {
this._isInTransaction = false;
}
}
那么整个调度事务到这就都执行完了。那么到这整个流程就回到了batchedMountComponentIntoNode
方法,接下来就是需要调用ReactUpdates.ReactReconcileTransaction.release
释放这个调度事务。
// ReactMount.js
function batchedMountComponentIntoNode(componentInstance, container, shouldReuseMarkup, context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
!shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement);
transaction.perform(mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
那么到这整个batchedMountComponentIntoNode
方法就算执行完了,但是这个方法是ReactDefaultBatchingStrategyTransaction
事务的回调。根据事务的规矩,执行完回调就需要执行close
了。
// ReactDefaultBatchingStrategy.js
var RESET_BATCHED_UPDATES = {
/**/
close: function () {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
}
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};
那么第一个就不说了,设置一下isBatchingUpdates
,第二个才是关键,调用了ReactUpdates.flushBatchedUpdates
刷新更新队列。下面的东西就是更新阶段的事情了,因为我们在挂载阶段的两个生命周期调用了setState
,当前dirtyComponents
中包含两项,这个flushBatchedUpdates
就是要刷新dirtyComponents
来完成更新。更新阶段的事下回再说。