Redux应用中每次调用reducer时深拷贝state有哪些弊端?
每次reducer执行时全量深拷贝state的常见问题
这种写法除了容易被直观感知的“大对象拷贝慢”之外,还有很多实际开发中踩过的坑,核心问题可以分为几类:
- 无意义的同步性能阻塞
示例里用的JSON.parse(JSON.stringify(state))是完全同步的全量遍历操作,每次action触发都会阻塞JS主线程,完成全对象序列化、反序列化的完整流程。哪怕你这次action根本不需要更新任何嵌套状态,这个流程的开销也会跟着state的复杂度线性上涨。
更明显的浪费来自现有代码的逻辑漏洞:不管走哪个switch分支,第一行的深拷贝已经执行完毕,哪怕最后走到default分支返回原state,前面花成本拷贝出来的对象会直接被丢弃,没有任何实际价值。 - 深拷贝本身的语义兼容性问题
用JSON方法实现的深拷贝,根本没法正确处理所有合法的JS值类型:state里如果存了函数、类实例、Map、Set、Symbol、带循环引用的对象,要么会直接拷贝失败抛错,要么会拷贝出完全不符合预期的值(比如Map/Set会被转成空普通对象、类实例的原型链会丢失、函数直接被抹除)。
就算换成lodashcloneDeep这类更完善的深拷贝工具,对非纯字面量类型的拷贝也会产生额外开销,甚至会打断原有业务逻辑中对特定对象的引用关系——比如你state里存了Websocket实例、第三方SDK的句柄,深拷贝出来的对象根本不是原来的有效引用,会直接导致业务逻辑失效。 - 彻底破坏Redux生态的内置优化机制
不管是React-Redux的useSelector、还是reselect的记忆化选择器,核心的性能优化逻辑完全依赖状态切片的引用稳定性:只有当你订阅的那部分状态的引用发生变化时,才会触发重渲染、重计算。
如果你每次action都对整个state做全量深拷贝,整棵状态树里所有的嵌套属性都会获得全新的引用——哪怕这次action根本没改动某个切片的内容,这个切片的引用也变了。最终会导致所有依赖state的组件、选择器都判定“状态有更新”,触发大量完全不必要的重渲染和重计算,应用的交互流畅度会随着state复杂度上升快速劣化。 - 完全违背Redux不可变更新模式的设计初衷
Redux推荐的最小范围浅拷贝更新,本质是在保证状态不可变的前提下,只对需要修改的嵌套节点创建新引用,其余未改动的节点全部保留原有引用,把拷贝的开销压到最低。全量深拷贝相当于为了省写逐层浅拷贝的精力,放弃了整个设计模式带来的所有性能收益,是典型的得不偿失的写法。
内容的提问来源于stack exchange,提问作者Michael Wolf Hoffman
相关产品推荐
相关产品推荐

