
关于
掌握现代 Angular 状态管理,包括 Signals、NgRx 和 RxJS。适用于设置全局状态、管理组件 Store、选择状态方案或从旧模式迁移。
name: angular-state-management description: "掌握现代 Angular 状态管理,包括 Signals、NgRx 和 RxJS。适用于设置全局状态、管理组件 store、选择状态方案或从旧模式迁移。" risk: safe source: self date_added: "2026-02-27"
Angular 状态管理
现代 Angular 状态管理模式的全面指南,从基于 Signal 的本地状态到全局 store 和服务端状态同步。
何时使用此技能
- 在 Angular 中设置全局状态管理
- 在 Signals、NgRx 或 Akita 之间做选择
- 管理组件级 store
- 实现乐观更新
- 调试状态相关问题
- 从旧状态模式迁移
不要在以下情况使用此技能
- 任务与 Angular 状态管理无关
- 你需要 React 状态管理 → 使用
react-state-management
核心概念
状态分类
| 类型 | 描述 | 解决方案 |
|------|------|---------|
| 本地状态 | 组件特定的 UI 状态 | Signals、signal() |
| 共享状态 | 相关组件之间 | Signal 服务 |
| 全局状态 | 应用级别、复杂 | NgRx、Akita、Elf |
| 服务端状态 | 远程数据、缓存 | NgRx Query、RxAngular |
| URL 状态 | 路由参数 | ActivatedRoute |
| 表单状态 | 输入值、验证 | Reactive Forms |
选择标准
小型应用,简单状态 → Signal Services
中型应用,中等状态 → Component Stores
大型应用,复杂状态 → NgRx Store
大量服务端交互 → NgRx Query + Signal Services
实时更新 → RxAngular + Signals
快速开始:基于 Signal 的状态
模式 1:简单 Signal 服务
// services/counter.service.ts
import { Injectable, signal, computed } from "@angular/core";
@Injectable({ providedIn: "root" })
export class CounterService {
// 私有可写 signals
private _count = signal(0);
// 公开只读
readonly count = this._count.asReadonly();
readonly doubled = computed(() => this._count() * 2);
readonly isPositive = computed(() => this._count() > 0);
increment() {
this._count.update((v) => v + 1);
}
decrement() {
this._count.update((v) => v - 1);
}
reset() {
this._count.set(0);
}
}
// 在组件中使用
@Component({
template: `
<p>Count: {{ counter.count() }}</p>
<p>Doubled: {{ counter.doubled() }}</p>
<button (click)="counter.increment()">+</button>
`,
})
export class CounterComponent {
counter = inject(CounterService);
}
模式 2:功能 Signal Store
// stores/user.store.ts
import { Injectable, signal, computed, inject } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { toSignal } from "@angular/core/rxjs-interop";
interface User {
id: string;
name: string;
email: string;
}
interface UserState {
user: User | null;
loading: boolean;
error: string | null;
}
@Injectable({ providedIn: "root" })
export class UserStore {
private http = inject(HttpClient);
// 状态 signals
private _user = signal<User | null>(null);
private _loading = signal(false);
private _error = signal<string | null>(null);
// 选择器(只读计算属性)
readonly user = computed(() => this._user());
readonly loading = computed(() => this._loading());
readonly error = computed(() => this._error());
readonly isAuthenticated = computed(() => this._user() !== null);
readonly displayName = computed(() => this._user()?.name ?? "Guest");
// 操作
async loadUser(id: string) {
this._loading.set(true);
this._error.set(null);
try {
const user = await fetch(`/api/users/${id}`).then((r) => r.json());
this._user.set(user);
} catch (e) {
this._error.set("Failed to load user");
} finally {
this._loading.set(false);
}
}
updateUser(updates: Partial<User>) {
this._user.update((user) => (user ? { ...user, ...updates } : null));
}
logout() {
this._user.set(null);
this._error.set(null);
}
}
模式 3:SignalStore(NgRx Signals)
// stores/products.store.ts
import {
signalStore,
withState,
withMethods,
withComputed,
patchState,
} from "@ngrx/signals";
import { inject } from "@angular/core";
import { ProductService } from "./product.service";
interface ProductState {
products: Product[];
loading: boolean;
filter: string;
}
const initialState: ProductState = {
products: [],
loading: false,
filter: "",
};
export const ProductStore = signalStore(
{ providedIn: "root" },
withState(initialState),
withComputed((store) => ({
filteredProducts: computed(() => {
const filter = store.filter().toLowerCase();
return store
.products()
.filter((p) => p.name.toLowerCase().includes(filter));
}),
})),
);
兼容工具
Claude CodeCursor
标签
前端开发