外观
Angular 状态管理
状态管理概念
状态管理是管理应用数据 (状态) 和状态变化的系统化方法,确保数据在组件之间保持一致性和可预测性。
示意图:
应用状态组成:
┌─────────────────┐
│ 全局状态 │ ← 多个组件共享
│ - 用户信息 │
│ - 应用配置 │
│ - 缓存数据 │
└─────────────────┘
┌─────────────────┐
│ 局部状态 │ ← 单个组件私有
│ - 表单数据 │
│ - UI状态 │
│ - 加载状态 │
└─────────────────┘状态管理挑战
组件通信问题
在大型应用中,组件间状态共享变得复杂。
示意图:
状态传递困境:
组件A (有状态)
↓ props
组件B
↓ props
组件C (需要状态)
↑ props
组件D (修改状态)
问题:多层传递、难以维护、状态分散状态分散问题
状态逻辑分散在不同组件中,导致不一致性。
示意图:
状态分散:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 组件A │ │ 组件B │ │ 组件C │
│ 状态副本 │ │ 状态副本 │ │ 状态副本 │
└─────────┘ └─────────┘ └─────────┘
各自维护,可能不一致服务状态管理
基础服务模式
使用 Angular 服务作为单一数据源管理状态。
示意图:
服务状态中心:
┌─────────────┐
│ 状态服务 │ ← 单一数据源
│ ┌─────────┐ │
│ │ 状态对象 │ │
│ └─────────┘ │
└─────────────┘
/ | \
/ | \
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 组件A │ │ 组件B │ │ 组件C │
│ 读取状态 │ │ 更新状态 │ │ 监听变化 │
└─────────┘ └─────────┘ └─────────┘可观察状态模式
使用 RxJS BehaviorSubject 实现响应式状态管理。
示意图:
响应式状态流:
状态服务 → BehaviorSubject → 组件订阅
数据流:
状态更新 → next(value) → 订阅者接收新值实现示例:
typescript
@Injectable({ providedIn: 'root' })
export class UserStateService {
private userSubject = new BehaviorSubject<User | null>(null);
public user$ = this.userSubject.asObservable();
setUser(user: User): void {
this.userSubject.next(user);
}
}RxJS 状态管理
状态流模式
使用 RxJS 操作符管理复杂状态流。
示意图:
状态操作链:
数据源 → 转换 → 过滤 → 组合 → 状态输出
示例:
userData$ → map(transform) → filter(activeOnly) → combineLatest(profile$) → 最终状态状态组合
组合多个状态流创建衍生状态。
示意图:
状态组合:
┌─────────┐ ┌─────────┐
│ 状态A$ │ │ 状态B$ │
└─────────┘ └─────────┘
\ /
\ /
combineLatest
|
↓
┌─────────┐
│ 组合状态 │
└─────────┘NgRx 状态管理
Redux 模式
NgRx 实现 Redux 模式:单一数据源、状态只读、纯函数更新。
示意图:
Redux 数据流:
Action
↓
Reducer (纯函数)
↓
Store (单一状态树)
↓
Selectors (状态选择)
↓
Components (组件)核心概念
NgRx 架构的核心组成部分。
示意图:
NgRx 结构:
┌─────────────────┐
│ Actions │ ← 描述状态变化
│ - 类型 │
│ - 载荷 │
├─────────────────┤
│ Reducers │ ← 纯函数处理状态
│ - 当前状态 │
│ - Action │
│ - 新状态 │
├─────────────────┤
│ Store │ ← 状态容器
│ - 状态树 │
│ - 派发Actions │
├─────────────────┤
│ Selectors │ ← 状态查询
│ - 状态派生 │
│ - 记忆化计算 │
└─────────────────┘数据流详细流程
完整的 NgRx 状态更新周期。
示意图:
NgRx 数据流:
组件 → Action → Reducer → Store → Selector → 组件更新
详细步骤:
1. 组件 dispatch(Action)
2. Action 触发 Reducer
3. Reducer 返回新状态
4. Store 更新状态
5. Selector 通知订阅者
6. 组件接收新状态组件本地状态
本地状态管理
使用组件内状态管理本地 UI 状态。
示意图:
组件内部状态:
┌─────────────────┐
│ 组件类 │
│ ┌─────────────┐ │
│ │ 本地状态 │ │
│ │ - loading │ │
│ │ - formData │ │
│ │ - isOpen │ │
│ └─────────────┘ │
└─────────────────┘智能与展示组件
分离状态逻辑和 UI 展示的组件设计模式。
示意图:
组件分离:
┌─────────────────┐ @Input() ┌─────────────────┐
│ 智能组件 │ ─────────────> │ 展示组件 │
│ - 管理状态 │ │ - 纯UI展示 │
│ - 处理业务逻辑 │ <───────────── │ - 无状态 │
│ - 数据获取 │ @Output() │ - 事件发射 │
└─────────────────┘ └─────────────────┘状态持久化
本地存储集成
将状态持久化到浏览器存储中。
示意图:
状态持久化:
应用状态 → 序列化 → localStorage → 反序列化 → 恢复状态
流程:
状态变化 → 自动保存 → 页面刷新 → 自动恢复路由状态管理
使用路由参数管理页面级状态。
示意图:
路由状态:
URL参数 → 路由解析 → 组件状态
示例:
/products?category=electronics&page=2
↓
组件读取路由参数初始化状态状态管理策略选择
方案对比
不同状态管理方案的适用场景。
示意图:
方案选择指南:
┌──────────────┬──────────────┬──────────────┐
│ 方案 │ 适用场景 │ 复杂度 │
├──────────────┼──────────────┼──────────────┤
│ 服务 + Subject │ 中小型应用 │ 低 │
│ 服务 + RxJS │ 中大型应用 │ 中 │
│ NgRx │ 大型复杂应用 │ 高 │
└──────────────┴──────────────┴──────────────┘混合策略
根据应用不同部分使用不同的状态管理方式。
示意图:
混合策略:
┌─────────────┐
│ 全局状态 │ ← NgRx (复杂业务)
└─────────────┘
┌─────────────┐
│ 功能状态 │ ← RxJS服务 (中等复杂度)
└─────────────┘
┌─────────────┐
│ UI状态 │ ← 组件本地状态 (简单)
└─────────────┘状态调试与工具
开发工具
使用 Redux DevTools 进行状态调试。
示意图:
调试流程:
状态变化 → 动作记录 → 时间旅行调试
工具功能:
- 查看状态历史
- 重放动作
- 状态快照比较不可变更新模式
使用不可变数据确保状态更新的可预测性。
示意图:
不可变更新:
原始状态 → 创建副本 → 修改副本 → 新状态
正确做法:
return { ...state, user: action.user }
错误做法:
state.user = action.user // 直接修改
return state最佳实践
状态规范化
规范化状态结构,避免数据冗余和嵌套。
示意图:
状态结构对比:
非规范化: 规范化:
{ {
posts: [ posts: {
{ id:1, user: {..}}, byId: { 1: {..}, 2: {..} },
{ id:2, user: {..}} allIds: [1, 2]
] },
} users: { ... }副作用管理
使用 Effects 或 RxJS 管理异步操作和副作用。
示意图:
副作用处理:
Action → Effect → 异步操作 → 新Action → Reducer
示例:
LoadUsers → API调用 → LoadUsersSuccess → 更新状态