NgRx多次触发dispatch致浏览器无响应,如何每秒更新多元素无性能问题?
性能问题根因
- 现有reducer中使用
cloneDeep全量深拷贝状态是核心性能瓶颈:每次更新都会完整复制整个状态树,每秒30次更新的场景下,状态体积越大拷贝开销越高,频繁的内存申请和GC会直接导致应用卡顿崩溃。 - 单元素单独触发dispatch,会导致reducer重复执行、状态变更通知多次广播,所有订阅NgRx状态的组件都会触发重复的变更检测,和PixiJS的渲染开销叠加后超出性能阈值。
- 地图元素频繁销毁重建额外增加了Pixi渲染线程的压力,进一步放大性能问题。
优化方案
1. 重构Reducer,取消全量深拷贝
NgRx要求的不可变更新不需要全量深拷贝整个状态,仅需要复制变更路径上的节点即可,将reducer修改为如下实现:
function updateRotation(state: State, callSing: string, rotation: number): State { return { ...state, elementRotation: { ...state.elementRotation, [callSing]: rotation } }; }
仅对变更的字段做扩展复制,避免无意义的全量状态拷贝,可降低90%以上的reducer执行开销
2. 合并批量更新,降低Dispatch频次
不要每个元素单独触发dispatch,将每秒30次的单元素更新合并为1次批量提交:
- 新增批量更新Action,payload定义为
Record<string, number>格式,存储所有呼号和对应旋转值的映射 - 新增配套批量更新reducer:
function updateBatchRotations(state: State, rotationMap: Record<string, number>): State { return { ...state, elementRotation: { ...state.elementRotation, ...rotationMap } }; }
- 本地暂存每秒采集到的所有元素更新数据,每秒仅触发1次批量dispatch,直接将dispatch频次从每秒30次降至1次。
3. 优化元素复用与状态订阅
- 缓存所有Pixi元素实例,仅在元素新增/删除时修改实例池,位置、旋转属性变更直接修改现有实例的对应属性即可,不要反复销毁重建元素。
- 不要在每个地图元素组件单独订阅NgRx状态,改为父组件统一订阅全量旋转值数据后批量下发,或者直接在Pixi的tick回调中读取最新状态更新元素属性,避免大量订阅导致的重复通知开销。
4. 可选进阶优化
如果后续元素量级继续上涨,可额外做以下调整:
- 使用NgRx的selector缓存
elementRotation的计算结果,搭配distinctUntilChanged过滤无意义的变更通知。 - 如果不需要在Angular模板中响应旋转值变更,可以将状态更新逻辑放到NgZone外执行,避免触发全局变更检测。
内容的提问来源于stack exchange,提问作者D.Zet
相关产品推荐
相关产品推荐

