NativeScript 状态管理 #
状态管理概述 #
状态管理是管理应用数据流的核心,良好的状态管理能让应用更易维护和扩展。
text
┌─────────────────────────────────────────────────────────────┐
│ 状态管理方案 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 基础方案 │
│ ├── Observable NativeScript 内置 │
│ └── Service + Subject Angular 服务 │
│ │
│ 集中式状态管理 │
│ ├── NgRx Angular Redux │
│ ├── Vuex Vue 状态管理 │
│ └── Redux React 状态管理 │
│ │
└─────────────────────────────────────────────────────────────┘
Observable 状态管理 #
基本状态管理 #
typescript
// viewmodels/app.viewmodel.ts
import { Observable, fromObject } from '@nativescript/core';
export class AppViewModel extends Observable {
private _user: User | null = null;
private _isLoggedIn: boolean = false;
private _isLoading: boolean = false;
get user(): User | null {
return this._user;
}
set user(value: User | null) {
if (this._user !== value) {
this._user = value;
this.notifyPropertyChange('user', value);
}
}
get isLoggedIn(): boolean {
return this._isLoggedIn;
}
set isLoggedIn(value: boolean) {
if (this._isLoggedIn !== value) {
this._isLoggedIn = value;
this.notifyPropertyChange('isLoggedIn', value);
}
}
get isLoading(): boolean {
return this._isLoading;
}
set isLoading(value: boolean) {
if (this._isLoading !== value) {
this._isLoading = value;
this.notifyPropertyChange('isLoading', value);
}
}
async login(email: string, password: string): Promise<void> {
this.isLoading = true;
try {
const user = await authService.login(email, password);
this.user = user;
this.isLoggedIn = true;
} finally {
this.isLoading = false;
}
}
logout(): void {
this.user = null;
this.isLoggedIn = false;
}
}
全局状态服务 #
typescript
// services/state.service.ts
import { Injectable } from '@angular/core';
import { Observable } from '@nativescript/core';
interface AppState {
user: User | null;
isLoggedIn: boolean;
theme: 'light' | 'dark';
}
@Injectable({
providedIn: 'root'
})
export class StateService extends Observable {
private state: AppState = {
user: null,
isLoggedIn: false,
theme: 'light'
};
getUser(): User | null {
return this.state.user;
}
setUser(user: User | null): void {
this.state.user = user;
this.state.isLoggedIn = !!user;
this.notifyPropertyChange('user', user);
this.notifyPropertyChange('isLoggedIn', this.state.isLoggedIn);
}
isLoggedIn(): boolean {
return this.state.isLoggedIn;
}
getTheme(): 'light' | 'dark' {
return this.state.theme;
}
setTheme(theme: 'light' | 'dark'): void {
this.state.theme = theme;
this.notifyPropertyChange('theme', theme);
}
}
Angular + RxJS #
使用 BehaviorSubject #
typescript
// services/store.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
interface AppState {
user: User | null;
products: Product[];
cart: CartItem[];
isLoading: boolean;
}
const initialState: AppState = {
user: null,
products: [],
cart: [],
isLoading: false
};
@Injectable({
providedIn: 'root'
})
export class StoreService {
private state = new BehaviorSubject<AppState>(initialState);
getState(): Observable<AppState> {
return this.state.asObservable();
}
getCurrentState(): AppState {
return this.state.getValue();
}
setState(partialState: Partial<AppState>): void {
this.state.next({
...this.state.getValue(),
...partialState
});
}
// 选择器
selectUser(): Observable<User | null> {
return new Observable(observer => {
this.state.subscribe(state => {
observer.next(state.user);
});
});
}
selectCart(): Observable<CartItem[]> {
return new Observable(observer => {
this.state.subscribe(state => {
observer.next(state.cart);
});
});
}
selectCartTotal(): Observable<number> {
return new Observable(observer => {
this.state.subscribe(state => {
const total = state.cart.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
observer.next(total);
});
});
}
}
使用服务 #
typescript
// services/user.service.ts
import { Injectable } from '@angular/core';
import { StoreService } from './store.service';
import { ApiService } from './api.service';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(
private store: StoreService,
private api: ApiService
) {}
async login(email: string, password: string): Promise<void> {
this.store.setState({ isLoading: true });
try {
const user = await this.api.post<User>('/auth/login', { email, password });
this.store.setState({ user, isLoading: false });
} catch (error) {
this.store.setState({ isLoading: false });
throw error;
}
}
logout(): void {
this.store.setState({ user: null });
}
}
组件中使用 #
typescript
// components/home/home.component.ts
import { Component, OnInit } from '@angular/core';
import { StoreService } from '../../services/store.service';
@Component({
selector: 'ns-home',
templateUrl: './home.component.html'
})
export class HomeComponent implements OnInit {
user$: Observable<User | null>;
cart$: Observable<CartItem[]>;
constructor(private store: StoreService) {
this.user$ = this.store.selectUser();
this.cart$ = this.store.selectCart();
}
ngOnInit() {}
}
NgRx 状态管理 #
安装 NgRx #
bash
npm install @ngrx/store @ngrx/effects @ngrx/store-devtools
定义状态 #
typescript
// store/user/user.model.ts
export interface User {
id: number;
name: string;
email: string;
}
// store/user/user.state.ts
export interface UserState {
user: User | null;
isLoading: boolean;
error: string | null;
}
export const initialState: UserState = {
user: null,
isLoading: false,
error: null
};
定义 Actions #
typescript
// store/user/user.actions.ts
import { createAction, props } from '@ngrx/store';
export const login = createAction(
'[User] Login',
props<{ email: string; password: string }>()
);
export const loginSuccess = createAction(
'[User] Login Success',
props<{ user: User }>()
);
export const loginFailure = createAction(
'[User] Login Failure',
props<{ error: string }>()
);
export const logout = createAction('[User] Logout');
定义 Reducer #
typescript
// store/user/user.reducer.ts
import { createReducer, on } from '@ngrx/store';
import * as UserActions from './user.actions';
export const userReducer = createReducer(
initialState,
on(UserActions.login, (state) => ({
...state,
isLoading: true,
error: null
})),
on(UserActions.loginSuccess, (state, { user }) => ({
...state,
user,
isLoading: false,
error: null
})),
on(UserActions.loginFailure, (state, { error }) => ({
...state,
isLoading: false,
error
})),
on(UserActions.logout, (state) => ({
...state,
user: null,
error: null
}))
);
定义 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 * as UserActions from './user.actions';
import { AuthService } from '../../services/auth.service';
@Injectable()
export class UserEffects {
login$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.login),
mergeMap(({ email, password }) =>
this.authService.login(email, password).pipe(
map(user => UserActions.loginSuccess({ user })),
catchError(error => of(UserActions.loginFailure({ error: error.message })))
)
)
)
);
constructor(
private actions$: Actions,
private authService: AuthService
) {}
}
定义 Selectors #
typescript
// store/user/user.selectors.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { UserState } from './user.state';
export const selectUserState = createFeatureSelector<UserState>('user');
export const selectUser = createSelector(
selectUserState,
(state: UserState) => state.user
);
export const selectIsLoading = createSelector(
selectUserState,
(state: UserState) => state.isLoading
);
export const selectError = createSelector(
selectUserState,
(state: UserState) => state.error
);
export const selectIsLoggedIn = createSelector(
selectUser,
(user: User | null) => !!user
);
配置 Store #
typescript
// app.module.ts
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { userReducer } from './store/user/user.reducer';
import { UserEffects } from './store/user/user.effects';
@NgModule({
imports: [
NativeScriptModule,
StoreModule.forRoot({ user: userReducer }),
EffectsModule.forRoot([UserEffects]),
StoreDevtoolsModule.instrument()
]
})
export class AppModule {}
组件中使用 #
typescript
// components/login/login.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import * as UserActions from '../../store/user/user.actions';
import { selectIsLoading, selectError } from '../../store/user/user.selectors';
@Component({
selector: 'ns-login',
templateUrl: './login.component.html'
})
export class LoginComponent {
email: string = '';
password: string = '';
isLoading$ = this.store.select(selectIsLoading);
error$ = this.store.select(selectError);
constructor(private store: Store) {}
onLogin() {
this.store.dispatch(UserActions.login({
email: this.email,
password: this.password
}));
}
}
Vuex 状态管理 (Vue) #
安装 Vuex #
bash
npm install vuex
创建 Store #
typescript
// store/index.ts
import Vue from 'nativescript-vue';
import Vuex from 'vuex';
Vue.use(Vuex);
interface State {
user: User | null;
products: Product[];
cart: CartItem[];
isLoading: boolean;
}
export default new Vuex.Store({
state: {
user: null,
products: [],
cart: [],
isLoading: false
} as State,
getters: {
isLoggedIn: state => !!state.user,
cartTotal: state => state.cart.reduce(
(sum, item) => sum + item.price * item.quantity,
0
),
cartItemCount: state => state.cart.reduce(
(sum, item) => sum + item.quantity,
0
)
},
mutations: {
SET_USER(state, user) {
state.user = user;
},
SET_PRODUCTS(state, products) {
state.products = products;
},
ADD_TO_CART(state, product) {
const existingItem = state.cart.find(item => item.id === product.id);
if (existingItem) {
existingItem.quantity++;
} else {
state.cart.push({ ...product, quantity: 1 });
}
},
REMOVE_FROM_CART(state, productId) {
const index = state.cart.findIndex(item => item.id === productId);
if (index > -1) {
state.cart.splice(index, 1);
}
},
SET_LOADING(state, isLoading) {
state.isLoading = isLoading;
}
},
actions: {
async login({ commit }, { email, password }) {
commit('SET_LOADING', true);
try {
const user = await authService.login(email, password);
commit('SET_USER', user);
} finally {
commit('SET_LOADING', false);
}
},
async fetchProducts({ commit }) {
commit('SET_LOADING', true);
try {
const products = await api.getProducts();
commit('SET_PRODUCTS', products);
} finally {
commit('SET_LOADING', false);
}
},
addToCart({ commit }, product) {
commit('ADD_TO_CART', product);
},
removeFromCart({ commit }, productId) {
commit('REMOVE_FROM_CART', productId);
}
}
});
使用 Store #
vue
<!-- components/Home.vue -->
<template>
<Page>
<ActionBar title="Home" />
<GridLayout>
<StackLayout>
<Label :text="user ? user.name : 'Guest'" />
<Label :text="'Cart: ' + cartItemCount" />
<Button text="Add to Cart" @tap="addToCart" />
</StackLayout>
</GridLayout>
</Page>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
computed: {
...mapGetters(['isLoggedIn', 'cartItemCount']),
user() {
return this.$store.state.user;
}
},
methods: {
...mapActions(['addToCart']),
addToCart() {
this.$store.dispatch('addToCart', this.product);
}
}
};
</script>
最佳实践 #
状态设计原则 #
text
┌─────────────────────────────────────────────────────────────┐
│ 状态设计原则 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 单一数据源 │
│ 应用状态存储在单一 store 中 │
│ │
│ 2. 状态只读 │
│ 不能直接修改状态,只能通过 action/mutation │
│ │
│ 3. 纯函数修改 │
│ Reducer/Mutation 是纯函数 │
│ │
│ 4. 异步操作分离 │
│ 异步操作放在 Effects/Actions 中 │
│ │
└─────────────────────────────────────────────────────────────┘
性能优化 #
typescript
// 使用选择器避免重复计算
export const selectCartTotal = createSelector(
selectCart,
(cart: CartItem[]) => cart.reduce(
(sum, item) => sum + item.price * item.quantity,
0
)
);
// 组件中使用
this.cartTotal$ = this.store.select(selectCartTotal);
下一步 #
现在你已经掌握了状态管理,接下来学习 动画系统,了解如何添加动画效果!
最后更新:2026-03-29