欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > 详解React setState调用原理和批量更新的过程

详解React setState调用原理和批量更新的过程

2024/10/26 3:35:32 来源:https://blog.csdn.net/weixin_46714216/article/details/141871692  浏览:    关键词:详解React setState调用原理和批量更新的过程

1. React setState 调用的原理

setState目录

    • 1. React setState 调用的原理
    • 2. React setState 调用之后发生了什么?是同步还是异步?
    • 3. React中的setState批量更新的过程是什么?

在这里插入图片描述

具体的执行过程如下(源码级解析):

  • 首先调用了setState 入口函数,入口函数在这里就是充当一个分发器的角色,根据入参的不同,将其分发到不同的功能函数中去;
ReactComponent.prototype.setState = function (partialState, callback) {this.updater.enqueueSetState(this, partialState);if (callback) {this.updater.enqueueCallback(this, callback, 'setState');}
};
  • enqueueSetState 方法将新的 state 放进组件的状态队列里,并调用 enqueueUpdate 来处理将要更新的实例对象;
enqueueSetState: function (publicInstance, partialState) {// 根据 this 拿到对应的组件实例var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');// 这个 queue 对应的就是一个组件实例的 state 数组var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);queue.push(partialState);//  enqueueUpdate 用来处理当前的组件实例enqueueUpdate(internalInstance);
}
  • enqueueUpdate 方法中引出了一个关键的对象——batchingStrategy,该对象所具备的isBatchingUpdates 属性直接决定了当下是要走更新流程,还是应该排队等待;如果轮到执行,就调用 batchedUpdates 方法来直接发起更新流程。由此可以推测,batchingStrategy 或许正是 React 内部专门用于管控批量更新的对象。
function enqueueUpdate(component) {ensureInjected();// 注意这一句是问题的关键,isBatchingUpdates标识着当前是否处于批量创建/更新组件的阶段if (!batchingStrategy.isBatchingUpdates) {// 若当前没有处于批量创建/更新组件的阶段,则立即更新组件batchingStrategy.batchedUpdates(enqueueUpdate, component);return;}// 否则,先把组件塞入 dirtyComponents 队列里,让它“再等等”dirtyComponents.push(component);if (component._updateBatchNumber == null) {component._updateBatchNumber = updateBatchNumber + 1;}
}

注意:batchingStrategy 对象可以理解为“锁管理器”。这里的“锁”,是指 React 全局唯一的 isBatchingUpdates 变量,isBatchingUpdates 的初始值是 false,意味着“当前并未进行任何批量更新操作”。每当 React 调用 batchedUpdate 去执行更新动作时,会先把这个锁给“锁上”(置为 true),表明“现在正处于批量更新过程中”。当锁被“锁上”的时候,任何需要更新的组件都只能暂时进入 dirtyComponents 里排队等候下一次的批量更新,而不能随意“插队”。此处体现的“任务锁”的思想,是 React 面对大量状态仍然能够实现有序分批处理的基石。

2. React setState 调用之后发生了什么?是同步还是异步?

(1)React中setState后发生了什么

在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。

在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

如果在短时间内频繁setState。React会将state的改变压入栈中,在合适的时机,批量更新state和视图,达到提高性能的效果。

(2)setState 是同步还是异步的

假如所有setState是同步的,意味着每执行一次setState时(有可能一个同步代码中,多次setState),都重新vnode diff + dom修改,这对性能来说是极为不好的。如果是异步,则可以把一个同步代码中的多个setState合并成一次组件更新。所以默认是异步的,但是在一些情况下是同步的。

setState 并不是单纯同步/异步的,它的表现会因调用场景的不同而不同。在源码中,通过 isBatchingUpdates 来判断setState 是先存进 state 队列还是直接更新,如果值为 true 则执行异步操作,为 false 则直接更新。

  • 异步: 在 React 可以控制的地方,就为 true,比如在 React 生命周期事件和合成事件中,都会走合并操作,延迟更新的策略。
  • 同步: 在 React 无法控制的地方,比如原生事件,具体就是在 addEventListener 、setTimeout、setInterval 等事件中,就只能同步更新。

一般认为,做异步设计是为了性能优化、减少渲染次数:

  • setState设计为异步,可以显著的提升性能。如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的;最好的办法应该是获取到多个更新,之后进行批量更新;
  • 如果同步更新了state,但是还没有执行render函数,那么stateprops不能保持同步。stateprops不能保持一致性,会在开发中产生很多的问题;

3. React中的setState批量更新的过程是什么?

调用 setState 时,组件的 state 并不会立即改变, setState 只是把要修改的 state 放入一个队列, React 会优化真正的执行时机,并出于性能原因,会将 React 事件处理程序中的多次React 事件处理程序中的多次 setState 的状态修改合并成一次状态修改。 最终更新只产生一次组件及其子组件的重新渲染,这对于大型应用程序中的性能提升至关重要。

this.setState({count: this.state.count + 1    ===>    入队,[count+1的任务]
});
this.setState({count: this.state.count + 1    ===>    入队,[count+1的任务,count+1的任务]
});↓合并 state,[count+1的任务]↓执行 count+1的任务

需要注意的是,只要同步代码还在执行,“攒起来”这个动作就不会停止。(注:这里之所以多次 +1 最终只有一次生效,是因为在同一个方法中多次 setState 的合并动作不是单纯地将更新累加。比如这里对于相同属性的设置,React 只会为其保留最后一次的更新)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com