Ionic状态管理 #
一、状态管理概述 #
1.1 为什么需要状态管理 #
text
问题:
- 组件间数据共享困难
- 状态变化难以追踪
- 异步数据流复杂
- 代码难以维护
解决方案:
- 集中式状态管理
- 单向数据流
- 可预测的状态变化
1.2 状态管理方案 #
| 方案 | 复杂度 | 适用场景 |
|---|---|---|
| Service | 低 | 简单应用 |
| RxJS Subject | 中 | 中等应用 |
| NgRx | 高 | 大型应用 |
二、Service状态管理 #
2.1 基本Service #
typescript
// services/user.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
export interface User {
id: string;
name: string;
email: string;
}
@Injectable({
providedIn: 'root'
})
export class UserService {
private usersSubject = new BehaviorSubject<User[]>([]);
private currentUserSubject = new BehaviorSubject<User | null>(null);
users$ = this.usersSubject.asObservable();
currentUser$ = this.currentUserSubject.asObservable();
setUsers(users: User[]) {
this.usersSubject.next(users);
}
setCurrentUser(user: User | null) {
this.currentUserSubject.next(user);
}
addUser(user: User) {
const users = [...this.usersSubject.value, user];
this.usersSubject.next(users);
}
removeUser(id: string) {
const users = this.usersSubject.value.filter(u => u.id !== id);
this.usersSubject.next(users);
}
getUsers(): User[] {
return this.usersSubject.value;
}
getCurrentUser(): User | null {
return this.currentUserSubject.value;
}
}
2.2 使用Service状态 #
typescript
import { Component, OnInit } from '@angular/core';
import { UserService, User } from './user.service';
@Component({
selector: 'app-users',
templateUrl: 'users.page.html'
})
export class UsersPage implements OnInit {
users$: Observable<User[]>;
constructor(private userService: UserService) {
this.users$ = this.userService.users$;
}
ngOnInit() {
this.loadUsers();
}
loadUsers() {
// 从API获取数据
this.api.getUsers().subscribe(users => {
this.userService.setUsers(users);
});
}
addUser() {
this.userService.addUser({
id: '1',
name: '新用户',
email: 'user@example.com'
});
}
}
三、RxJS状态管理 #
3.1 状态Store #
typescript
// store/store.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, map } from 'rxjs';
export interface State {
users: User[];
currentUser: User | null;
isLoading: boolean;
error: string | null;
}
const initialState: State = {
users: [],
currentUser: null,
isLoading: false,
error: null
};
@Injectable({
providedIn: 'root'
})
export class Store {
private stateSubject = new BehaviorSubject<State>(initialState);
state$ = this.stateSubject.asObservable();
// 选择器
users$ = this.state$.pipe(map(state => state.users));
currentUser$ = this.state$.pipe(map(state => state.currentUser));
isLoading$ = this.state$.pipe(map(state => state.isLoading));
error$ = this.state$.pipe(map(state => state.error));
// 获取当前状态
getState(): State {
return this.stateSubject.value;
}
// 更新状态
setState(partialState: Partial<State>) {
this.stateSubject.next({
...this.getState(),
...partialState
});
}
// 重置状态
reset() {
this.stateSubject.next(initialState);
}
}
3.2 Actions #
typescript
// store/actions.ts
export enum ActionType {
LOAD_USERS = '[Users] Load Users',
LOAD_USERS_SUCCESS = '[Users] Load Users Success',
LOAD_USERS_FAILURE = '[Users] Load Users Failure',
ADD_USER = '[Users] Add User',
SET_CURRENT_USER = '[Users] Set Current User'
}
export interface Action {
type: ActionType;
payload?: any;
}
export const loadUsers = (): Action => ({
type: ActionType.LOAD_USERS
});
export const loadUsersSuccess = (users: User[]): Action => ({
type: ActionType.LOAD_USERS_SUCCESS,
payload: users
});
export const loadUsersFailure = (error: string): Action => ({
type: ActionType.LOAD_USERS_FAILURE,
payload: error
});
3.3 Reducer #
typescript
// store/reducer.ts
import { State, Action, ActionType } from './types';
export function reducer(state: State, action: Action): State {
switch (action.type) {
case ActionType.LOAD_USERS:
return {
...state,
isLoading: true,
error: null
};
case ActionType.LOAD_USERS_SUCCESS:
return {
...state,
users: action.payload,
isLoading: false
};
case ActionType.LOAD_USERS_FAILURE:
return {
...state,
isLoading: false,
error: action.payload
};
case ActionType.ADD_USER:
return {
...state,
users: [...state.users, action.payload]
};
case ActionType.SET_CURRENT_USER:
return {
...state,
currentUser: action.payload
};
default:
return state;
}
}
四、NgRx状态管理 #
4.1 安装NgRx #
bash
npm install @ngrx/store @ngrx/effects @ngrx/store-devtools
4.2 State定义 #
typescript
// store/user/user.state.ts
export interface UserState {
users: User[];
currentUser: User | null;
loading: boolean;
error: string | null;
}
export const initialState: UserState = {
users: [],
currentUser: null,
loading: false,
error: null
};
4.3 Actions #
typescript
// store/user/user.actions.ts
import { createAction, props } from '@ngrx/store';
import { User } from '../../models/user.model';
export const loadUsers = createAction('[Users] Load Users');
export const loadUsersSuccess = createAction('[Users] Load Users Success', props<{ users: User[] }>());
export const loadUsersFailure = createAction('[Users] Load Users Failure', props<{ error: string }>());
export const addUser = createAction('[Users] Add User', props<{ user: User }>());
export const setCurrentUser = createAction('[Users] Set Current User', props<{ user: User | null }>());
4.4 Reducer #
typescript
// store/user/user.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { UserState, initialState } from './user.state';
import * as UserActions from './user.actions';
export const userReducer = createReducer(
initialState,
on(UserActions.loadUsers, (state) => ({
...state,
loading: true,
error: null
})),
on(UserActions.loadUsersSuccess, (state, { users }) => ({
...state,
users,
loading: false
})),
on(UserActions.loadUsersFailure, (state, { error }) => ({
...state,
loading: false,
error
})),
on(UserActions.addUser, (state, { user }) => ({
...state,
users: [...state.users, user]
})),
on(UserActions.setCurrentUser, (state, { user }) => ({
...state,
currentUser: user
}))
);
4.5 Selectors #
typescript
// store/user/user.selectors.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { UserState } from './user.state';
export const selectUserState = createFeatureSelector<UserState>('users');
export const selectUsers = createSelector(
selectUserState,
(state) => state.users
);
export const selectCurrentUser = createSelector(
selectUserState,
(state) => state.currentUser
);
export const selectLoading = createSelector(
selectUserState,
(state) => state.loading
);
export const selectError = createSelector(
selectUserState,
(state) => state.error
);
export const selectUserById = (id: string) => createSelector(
selectUsers,
(users) => users.find(user => user.id === id)
);
4.6 Effects #
typescript
// store/user/user.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { UserService } from '../../services/user.service';
import * as UserActions from './user.actions';
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() => this.actions$.pipe(
ofType(UserActions.loadUsers),
mergeMap(() => this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({ error: error.message })))
))
));
constructor(
private actions$: Actions,
private userService: UserService
) {}
}
4.7 Store配置 #
typescript
// store/index.ts
import { ActionReducerMap } from '@ngrx/store';
import { UserState, userReducer } from './user/user.reducer';
export interface AppState {
users: UserState;
}
export const reducers: ActionReducerMap<AppState> = {
users: userReducer
};
// app.module.ts
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { reducers } from './store';
import { UserEffects } from './store/user/user.effects';
@NgModule({
imports: [
StoreModule.forRoot(reducers),
EffectsModule.forRoot([UserEffects]),
StoreDevtoolsModule.instrument({ maxAge: 25 })
]
})
export class AppModule {}
4.8 使用Store #
typescript
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { AppState } from './store';
import * as UserActions from './store/user/user.actions';
import * as UserSelectors from './store/user/user.selectors';
@Component({
selector: 'app-users',
templateUrl: 'users.page.html'
})
export class UsersPage implements OnInit {
users$: Observable<User[]>;
loading$: Observable<boolean>;
constructor(private store: Store<AppState>) {
this.users$ = this.store.select(UserSelectors.selectUsers);
this.loading$ = this.store.select(UserSelectors.selectLoading);
}
ngOnInit() {
this.store.dispatch(UserActions.loadUsers());
}
addUser() {
this.store.dispatch(UserActions.addUser({
user: {
id: '1',
name: '新用户',
email: 'user@example.com'
}
}));
}
}
五、最佳实践 #
5.1 状态设计原则 #
text
原则:
- 单一数据源
- 状态只读
- 纯函数修改
- 扁平化结构
5.2 性能优化 #
typescript
// 使用memoized选择器
export const selectUserCount = createSelector(
selectUsers,
(users) => users.length
);
// 使用distinctUntilChanged
this.users$.pipe(
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
);
六、总结 #
6.1 方案选择 #
| 应用规模 | 推荐方案 |
|---|---|
| 小型 | Service |
| 中型 | RxJS Subject |
| 大型 | NgRx |
6.2 下一步 #
掌握了状态管理后,接下来让我们学习 Capacitor基础,了解Ionic的原生功能!
最后更新:2026-03-28