<1> transaction 事务【合成事件and生命周期函数】
在React执行合成事件或者生命周期函数时,会使用一个Transaction对象将整个执行过程包裹成一个事务。在该事务中有一个
环境变量——isBatchingUpdates
。【可以看作是一把锁,来控制着 setState()的执行】它是实现合成事件and生命周期函数中的setState()是批量更新的关键。
因为事务的执行流程为:
-
在事务中会最先设置环境变量
isBatchingUpdates
为true -
而执行 setState() 之前,又总会去判断
isBatchingUpdates
的值- (1)如果为 true ,说明此时 React 正处于批量更新过程中 - 则不会立马执行此次state更新,而是会把其放入`_pendingStateQueue【等待执行更新的state队列】` - (2)如果为 false ,说明此时 React 并未进行任何批量更新操作 - 则会先去把`isBatchingUpdates`的值设为 true,把锁给锁上。 - 遍历待更新组件队列`dirtyComponent`依次执行更新里面的组件。 - 对于每个组件执行生命周期,并把`_pendingStateQueue【等待执行更新的state队列】`中,存放的该组件的 state 进行合并。 - 然后执行后续更新的生命周期!!!
-
该事务执行完毕后,又会把
isBatchingUpdates
的值设为 false
<2> setState()的执行流程
流程图如下:
大致过程为:
-
1.将setState传入的
partialState
参数【setState()传入的第一个参数】存储在当前组件实例的state暂存队列_pendingStateQueue
中。 -
2.判断
isBatchingUpdates
的值,从而判断当前React是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列dirtyComponent
中。 -
3.如果未处于批量更新状态,将
isBatchingUpdates
的值设置为true,进行上锁。用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中。 -
4.调用事务的
waper
方法,遍历待更新组件队列dirtyComponent
依次执行更新。 -
5.执行生命周期
componentWillReceiveProps
。 -
6.将组件的state暂存队列
_pendingStateQueue
中的state
进行合并,获得最终要更新的state对象,并将队列置为空。 -
7.执行生命周期
componentShouldUpdate
,根据返回值判断是否要继续更新。 -
8.执行生命周期
componentWillUpdate
。 -
9.执行真正的更新,
render
。 -
10.执行生命周期
componentDidUpdate
。
<3> 批量更新总结
上面两点很好的解释了,为什么一些时候 【在合成事件and生命周期函数中】,感觉 setState() 的执行是异步的,即 setState() 之后,打印 state 却拿不到它的最新值,还是旧值 【原生事件and异步函数中】,又觉得 setState() 的执行是同步的。
- 生命周期函数and合成事件中:
因为会被包裹成一个事务执行,而在事务的开始,就会设置 isBatchingUpdates
的值为 true ,就会进入批量更新!!!
- 原生事件and异步函数:
此时isBatchingUpdates
的值为 false ,不会进入批量更新!!!
<4> partialState
合并机制
来浅看一下,合并机制的源码:
_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];
}
// 重点:实现了如果setState()的参数是对象的形式,就会进行合并;如果是函数的形式,就不会!!!
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;
},