Angular网页游戏状态Store模式设计与RxJS适配方案问询
RxJS完全适配你的需求,是该场景下的最优解决方案
关于避免全量重渲染的问题
- 不建议采用深度遍历键值对修改原状态的方案:一方面如果你的组件使用Angular的
OnPush变更检测策略,修改原对象不会触发引用变更,组件无法感知到状态更新;另一方面状态树膨胀后,深度遍历比对的性能开销远高于直接替换全量状态+按需订阅的方案,性价比极低。 - 正确的处理逻辑是:保持现有的全量状态替换逻辑,上游每次收到后端返回的新状态直接替换旧状态的引用,交给RxJS的操作符处理下游的去重过滤。
关于状态子集监听的实现
RxJS的操作符可以非常低成本的实现这个需求,你只需要对现有的ConnectionService做小幅度改造即可:
- 把原有的
gameState对象改造为RxJS的BehaviorSubject,对外不直接暴露Subject,而是封装通用的select方法:
import { BehaviorSubject, map, distinctUntilChanged, Observable } from 'rxjs'; // 你的状态类型定义 interface GameState { pilot: { switches: boolean[]; // 其他字段 }; // 其他状态字段 } @Injectable({ providedIn: 'root' }) export class ConnectionService { // 内部维护状态Subject,初始值可设为空状态 private readonly gameState$ = new BehaviorSubject<GameState>({} as GameState); /** * 订阅状态树的指定子集,只有对应值发生变更时才会触发回调 * @param path 状态访问路径,比如要取pilot.switches[5]就传('pilot', 'switches', 5) */ public select<T>(...path: (string | number)[]): Observable<T> { return this.gameState$.pipe( map(state => path.reduce((curr, key) => curr?.[key], state) as T), // 只有当前值和上一次值不同时才向下游推送 distinctUntilChanged() ); } // 收到后端新状态时直接调用,不需要做任何比对修改 private onServerStateUpdate(newState: GameState): void { this.gameState$.next(newState); } // 原有sendEvent方法保留 public sendEvent(event: unknown): void { // 原有逻辑 } }
- 组件侧使用时,只需要订阅自己需要的状态子集即可,比如你要监听
gameState.pilot.switches[5]的变更:
@Component({ selector: 'app-pilot-panel', template: `<!-- 模板里可以直接用async管道订阅,自动管理订阅生命周期 --> <div *ngIf="switch5State$ | async as switchState">开关5状态:{{switchState}}</div>`, // 配合OnPush变更检测策略,只有订阅的状态变更时才会触发组件重渲染 changeDetection: ChangeDetectionStrategy.OnPush }) export class PilotPanelComponent { // 只订阅需要的状态字段,其他状态变更完全不会影响当前组件 readonly switch5State$ = this.connectionService.select<boolean>('pilot', 'switches', 5); constructor(private connectionService: ConnectionService) { // 也可以手动订阅执行自定义逻辑 this.switch5State$.subscribe(newVal => { // 只有switch5的值变化时才会执行逻辑X this.doSomething(newVal); }); } private doSomething(val: boolean): void { // 你的业务逻辑 } }
方案优势
- 不需要改造现有服务端的状态推送逻辑,客户端改造成本极低
- 配合Angular的
OnPush变更检测策略,可以完全避免无关状态变更导致的无效重渲染,10Hz的更新频率哪怕状态树膨胀到上百个字段也不会有性能问题 - 通用的
select方法可以满足任意层级的状态子集监听需求,不需要写额外的比对逻辑
内容的提问来源于stack exchange,提问作者yathern
相关产品推荐
相关产品推荐

