이 글은 주로 React의 setState 소스 코드에 대한 심층적인 연구를 소개합니다. 편집자는 꽤 좋다고 생각하므로 지금 공유하고 참고용으로 제공하겠습니다. 편집자를 따라 살펴보겠습니다. 모두에게 도움이 되기를 바랍니다.
프론트엔드 프레임워크인 React는 MVVM의 View 부분에만 중점을 두지만 여전히 View와 모델의 바인딩을 구현합니다. 데이터를 수정하는 동안 보기를 새로 고칠 수 있습니다. 이는 우리의 논리를 크게 단순화하고 데이터 흐름의 변경에만 관심을 가지면 되며 코드의 양도 줄어들고 나중에 유지 관리가 더 편리해집니다. 이 기능은 setState() 메소드 덕분입니다. React는 대기열 메커니즘을 사용하여 상태를 관리하므로 반복되는 View 새로 고침을 방지합니다. 소스 코드 관점에서 setState 메커니즘을 살펴보겠습니다.
1 무엇이 전달되고, 어떤 객체인지, React의 컨텍스트 업데이터가 어떻게 전달되는지는 아래 react 소스 코드 분석 시리즈 기사를 참조하세요.
여기서 직접 결과에 대해 이야기해 보겠습니다. 실제로 ReactUpdateQueue.js에 노출된 ReactUpdateQueue 객체입니다.
2 이제 setState 이후에 실행되는 작업을 찾았으므로 단계별로 진행하겠습니다
class App extends Component { //只在组件重新加载的时候执行一次 constructor(props) { super(props); //.. } //other methods } //ReactBaseClasses.js中如下:这里就是setState函数的来源; //super其实就是下面这个函数 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; } ReactComponent.prototype.setState = function (partialState, callback) { this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, 'setState'); } };
class Root extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { let me = this; me.setState({ count: me.state.count + 1 }); console.log(me.state.count); // 打印出0 me.setState({ count: me.state.count + 1 }); console.log(me.state.count); // 打印出0 setTimeout(function(){ me.setState({ count: me.state.count + 1 }); console.log(me.state.count); // 打印出2 }, 0); setTimeout(function(){ me.setState({ count: me.state.count + 1 }); console.log(me.state.count); // 打印出3 }, 0); } render() { return ( <h1>{this.state.count}</h1> ) } } ReactComponent.prototype.setState = function (partialState, callback) { this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, 'setState'); } };
var ReactUpdates = require('./ReactUpdates'); function enqueueUpdate(internalInstance) { ReactUpdates.enqueueUpdate(internalInstance); }; function getInternalInstanceReadyForUpdate(publicInstance, callerName) { //在ReactCompositeComponent.js中有这样一行代码,这就是其来源; // Store a reference from the instance back to the internal representation //ReactInstanceMap.set(inst, this); var internalInstance = ReactInstanceMap.get(publicInstance); //返回的是在ReactCompositeComponent.js中construct函数返回的对象;ReactInstance实例对象并不是简单的new 我们写的组件的实例对象,而是经过instantiateReactComponent.js中ReactCompositeComponentWrapper函数包装的对象;详见 创建React组件方式以及源码分析.md return internalInstance; }; var ReactUpdateQueue = { //。。。。省略其他代码 enqueueCallback: function (publicInstance, callback, callerName) { ReactUpdateQueue.validateCallback(callback, callerName); var internalInstance = getInternalInstanceReadyForUpdate(publicInstance); if (!internalInstance) { return null; } //这里将callback放入组件实例的_pendingCallbacks数组中; if (internalInstance._pendingCallbacks) { internalInstance._pendingCallbacks.push(callback); } else { internalInstance._pendingCallbacks = [callback]; } // TODO: The callback here is ignored when setState is called from // componentWillMount. Either fix it or disallow doing so completely in // favor of getInitialState. Alternatively, we can disallow // componentWillMount during server-side rendering. enqueueUpdate(internalInstance); }, enqueueSetState: function (publicInstance, partialState) { var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState'); if (!internalInstance) { return; } //这里,初始化queue变量,同时初始化 internalInstance._pendingStateQueue = [ ] ; //对于 || 的短路运算还是要多梳理下 //queue数组(模拟队列)中存放着setState放进来的对象; var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); //这里将partialState放入queue数组中,也就是internalInstance._pendingStateQueue 数组中,此时,每次setState的partialState,都放进了React组件实例对象上的_pendingStateQueue属性中,成为一个数组; queue.push(partialState); enqueueUpdate(internalInstance); }, }; module.exports = ReactUpdateQueue;
function enqueueUpdate(internalInstance) { ReactUpdates.enqueueUpdate(internalInstance); }
var dirtyComponents = []; var updateBatchNumber = 0; var asapCallbackQueue = CallbackQueue.getPooled(); var asapEnqueued = false; //这里声明batchingStrategy为null,后期通过注册给其赋值; var batchingStrategy = null; //这里的component参数是js中ReactCompositeComponentWrapper函数包装的后的React组件实例对象; function enqueueUpdate(component) { ensureInjected(); //第一次执行setState的时候,可以进入if语句,遇到里面的return语句,终止执行 //如果不是正处于创建或更新组件阶段,则处理update事务 if (!batchingStrategy.isBatchingUpdates) { //batchedUpdates就是ReactDefaultBatchingStrategy.js中声明的 batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } //第二次执行setState的时候,进入不了if语句,将组件放入dirtyComponents //如果正在创建或更新组件,则暂且先不处理update,只是将组件放在dirtyComponents数组中 dirtyComponents.push(component); if (component._updateBatchNumber == null) { component._updateBatchNumber = updateBatchNumber + 1; } }; //enqueueUpdate包含了React避免重复render的逻辑。mountComponent和updateComponent方法在执行的最开始,会调用到batchedUpdates进行批处理更新,此时会将isBatchingUpdates设置为true,也就是将状态标记为现在正处于更新阶段了。之后React以事务的方式处理组件update,事务处理完后会调用wrapper.close(), 而TRANSACTION_WRAPPERS中包含了RESET_BATCHED_UPDATES这个wrapper,故最终会调用RESET_BATCHED_UPDATES.close(), 它最终会将isBatchingUpdates设置为false。
//RESET_BATCHED_UPDATES用来管理isBatchingUpdates的状态 var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function () { // 事务批更新处理结束时,将isBatchingUpdates设为了false ReactDefaultBatchingStrategy.isBatchingUpdates = false; } }; //FLUSH_BATCHED_UPDATES会在一个transaction的close阶段运行runBatchedUpdates,从而执行update。 //因为close的执行顺序是FLUSH_BATCHED_UPDATES.close ==> 然后RESET_BATCHED_UPDATES.close 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; } }); //这个transition就是下面ReactDefaultBatchingStrategy对象中使用的transaction变量 var transaction = new ReactDefaultBatchingStrategyTransaction(); var ReactDefaultBatchingStrategy = { isBatchingUpdates: false, /** * Call the provided function in a context within which calls to `setState` * and friends are batched such that components aren't updated unnecessarily. */ batchedUpdates: function (callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; // 批处理最开始时,将isBatchingUpdates设为true,表明正在更新 ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) { return callback(a, b, c, d, e); } else { //transition在上面已经声明; // 以事务的方式处理updates,后面详细分析transaction return transaction.perform(callback, null, a, b, c, d, e); } } }; module.exports = ReactDefaultBatchingStrategy;
var _prodInvariant = require('./reactProdInvariant'); var invariant = require('fbjs/lib/invariant'); var OBSERVED_ERROR = {}; var TransactionImpl = { reinitializeTransaction: function () { //getTransactionWrappers这个函数ReactDefaultBatchingStrategy.js中声明的,上面有;返回一个数组; this.transactionWrappers = this.getTransactionWrappers(); if (this.wrapperInitData) { this.wrapperInitData.length = 0; } else { this.wrapperInitData = []; } this._isInTransaction = false; }, _isInTransaction: false, getTransactionWrappers: null, isInTransaction: function () { return !!this._isInTransaction; }, perform: function (method, scope, a, b, c, d, e, f) { var errorThrown; var ret; try { this._isInTransaction = true; errorThrown = true; //var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; //1 这里会先执行所有的TRANSACTION_WRAPPERS中成员的initialize方法,上面声明的其都是emptyFunction this.initializeAll(0); //2 这里其实还是执行的 enqueueUpdate 函数 ret = method.call(scope, a, b, c, d, e, f); errorThrown = 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. //3 执行TRANSACTION_WRAPPERS对象中成员的所有close方法; this.closeAll(0); } } finally { this._isInTransaction = false; } } return ret; }, initializeAll: function (startIndex) { var transactionWrappers = this.transactionWrappers; for (var i = startIndex; i < transactionWrappers.length; i++) { var wrapper = transactionWrappers[i]; try { this.wrapperInitData[i] = OBSERVED_ERROR; this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null; } finally { if (this.wrapperInitData[i] === OBSERVED_ERROR) { try { this.initializeAll(i + 1); } catch (err) {} } } } }, closeAll: function (startIndex) { var transactionWrappers = this.transactionWrappers; for (var i = startIndex; i < transactionWrappers.length; i++) { var wrapper = transactionWrappers[i]; var initData = this.wrapperInitData[i]; var errorThrown; try { errorThrown = true; if (initData !== OBSERVED_ERROR && wrapper.close) { wrapper.close.call(this, initData); } errorThrown = false; } finally { if (errorThrown) { try { this.closeAll(i + 1); } catch (e) {} } } } this.wrapperInitData.length = 0; } }; module.exports = TransactionImpl //3 执行TRANSACTION_WRAPPERS对象中成员的所有close方法; var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) };
var flushBatchedUpdates = function () { while (dirtyComponents.length || asapEnqueued) { if (dirtyComponents.length) { var transaction = ReactUpdatesFlushTransaction.getPooled(); //这里执行runBatchedUpdates函数; transaction.perform(runBatchedUpdates, null, transaction); ReactUpdatesFlushTransaction.release(transaction); } if (asapEnqueued) { asapEnqueued = false; var queue = asapCallbackQueue; asapCallbackQueue = CallbackQueue.getPooled(); queue.notifyAll(); CallbackQueue.release(queue); } } }; function runBatchedUpdates(transaction) { var len = transaction.dirtyComponentsLength; dirtyComponents.sort(mountOrderComparator); updateBatchNumber++; for (var i = 0; i < len; i++) { var component = dirtyComponents[i]; var callbacks = component._pendingCallbacks; component._pendingCallbacks = null; var markerName; if (ReactFeatureFlags.logTopLevelRenders) { var namedComponent = component; // Duck type TopLevelWrapper. This is probably always true. if (component._currentElement.type.isReactTopLevelWrapper) { namedComponent = component._renderedComponent; } markerName = 'React update: ' + namedComponent.getName(); console.time(markerName); } //这里才是真正的开始更新组件 ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction, updateBatchNumber); if (markerName) { console.timeEnd(markerName); } if (callbacks) { for (var j = 0; j < callbacks.length; j++) { transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance()); } } } }
performUpdateIfNecessary: function (internalInstance, transaction, updateBatchNumber) { if (internalInstance._updateBatchNumber !== updateBatchNumber) { // The component's enqueued batch number should always be the current // batch or the following one. return; } //这里执行React组件实例对象的更新;internalInstance上的performUpdateIfNecessary在ReactCompositeComponent.js中的; internalInstance.performUpdateIfNecessary(transaction); if (process.env.NODE_ENV !== 'production') { if (internalInstance._debugID !== 0) { ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID); } } }
performUpdateIfNecessary: function (transaction) { if (this._pendingElement != null) { // receiveComponent会最终调用到updateComponent,从而刷新View ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context); } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) { // 执行updateComponent,从而刷新View。 this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context); } else { this._updateBatchNumber = null; } }, //执行更新React组件的props. state。context函数 updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) { var inst = this._instance; var willReceive = false; var nextContext; // Determine if the context has changed or not if (this._context === nextUnmaskedContext) { nextContext = inst.context; } else { nextContext = this._processContext(nextUnmaskedContext); willReceive = true; } var prevProps = prevParentElement.props; var nextProps = nextParentElement.props; // Not a simple state update but a props update if (prevParentElement !== nextParentElement) { willReceive = true; } // An update here will schedule an update but immediately set // _pendingStateQueue which will ensure that any state updates gets // immediately reconciled instead of waiting for the next batch. if (willReceive && inst.componentWillReceiveProps) { if (process.env.NODE_ENV !== 'production') { measureLifeCyclePerf(function () { return inst.componentWillReceiveProps(nextProps, nextContext); }, this._debugID, 'componentWillReceiveProps'); } else { inst.componentWillReceiveProps(nextProps, nextContext); } } //这里可以知道为什么setState可以接受函数,主要就是_processPendingState函数; //这里仅仅是将每次setState放入到_pendingStateQueue队列中的值,合并到nextState,并没有真正的更新state的值;真正更新组件的state的值是在下面; var nextState = this._processPendingState(nextProps, nextContext); var shouldUpdate = true; if (!this._pendingForceUpdate) { if (inst.shouldComponentUpdate) { if (process.env.NODE_ENV !== 'production') { shouldUpdate = measureLifeCyclePerf(function () { return inst.shouldComponentUpdate(nextProps, nextState, nextContext); }, this._debugID, 'shouldComponentUpdate'); } else { shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext); } } else { if (this._compositeType === CompositeTypes.PureClass) { shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); } } } this._updateBatchNumber = null; if (shouldUpdate) { this._pendingForceUpdate = false; // Will set `this.props`, `this.state` and `this.context`. this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext); } else { // If it's determined that a component should not update, we still want // to set props and state but we shortcut the rest of the update. //诺:在这里更新组件的state. props 等值; this._currentElement = nextParentElement; this._context = nextUnmaskedContext; inst.props = nextProps; inst.state = nextState; inst.context = nextContext; } }, _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]; //如果是setState的参数是一个函数,那么该函数接受三个参数,分别是state props context _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial); } return nextState; },
enqueueSetState는 상태를 대기열에 넣고 enqueueUpdate를 호출하여 업데이트할 구성 요소를 처리합니다.
위 내용은 React의 setState 소스 코드에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!