使用Angular和NgRx进行状态管理

技术趋势洞察 2020-03-10 ⋅ 14 阅读

在Angular应用中,状态管理是一个非常重要的概念。当应用变得复杂时,很容易出现难以理解和维护的状态代码。为了解决这个问题,我们可以使用NgRx,它是一个基于Redux的状态管理库。本篇文章将介绍如何使用Angular和NgRx进行状态管理,以及如何优化应用的性能和可维护性。

安装NgRx

在开始之前,我们需要先安装NgRx。打开终端,进入你的Angular项目根目录,运行以下命令来安装NgRx:

npm install @ngrx/store @ngrx/effects --save

上述命令将会安装NgRx的状态管理库和副作用库。

创建状态

接下来,我们需要创建一些状态来管理我们的应用程序数据。在Angular中,我们可以使用状态来存储和管理组件之间共享的数据。

打开src/app文件夹,创建一个名为store的文件夹。在store文件夹中,我们将创建一个名为app.state.ts的文件,并在其中定义我们的应用程序状态。

// src/app/store/app.state.ts

export interface AppState {
  // 定义你的状态属性
  user: User;
  products: Product[];
  // ...
}

在上述代码中,我们定义了一个AppState接口,它包含了我们应用程序的状态属性。你可以根据你的应用程序需求进行相应的更改和调整。

除了状态属性之外,我们还需要定义相应的模型类。

// src/app/models/user.model.ts

export interface User {
  id: number;
  name: string;
  email: string;
  // ...
}

// src/app/models/product.model.ts

export interface Product {
  id: number;
  name: string;
  price: number;
  // ...
}

在上述代码中,我们定义了UserProduct这两个模型类,用来描述用户和产品信息的数据结构。你可以根据你的应用程序需求进行相应的更改和调整。

创建Actions

现在我们需要创建一些Actions,来描述我们的应用程序状态的变化。

store文件夹中,我们将创建一个名为app.actions.ts的文件,并在其中定义我们的应用程序Actions。

// src/app/store/app.actions.ts

import { createAction, props } from '@ngrx/store';
import { User, Product } from '../models';

// User Actions
export const setUser = createAction('[User] Set User', props<{ user: User }>());

// Product Actions
export const addProduct = createAction('[Product] Add Product', props<{ product: Product }>());
export const removeProduct = createAction('[Product] Remove Product', props<{ id: number }>());

在上述代码中,我们使用createAction函数来创建我们的Actions。createAction函数接受两个参数:Action的类型和包含在Action中的属性定义。

在我们的示例中,我们创建了三个Actions,分别是setUseraddProductremoveProduct。你可以根据你的应用程序需求创建相应的Actions。

创建Reducers

现在我们需要创建Reducers来处理Actions对应的状态变化。Reducers是纯函数,它接受应用程序当前的状态和一个Action作为输入,并返回一个新的状态。

store文件夹中,我们将创建一个名为app.reducer.ts的文件,并在其中定义我们的应用程序Reducers。

// src/app/store/app.reducer.ts

import { Action, createReducer, on } from '@ngrx/store';
import * as appActions from './app.actions';
import { AppState, User, Product } from '../models';

const initialState: AppState = {
  user: null,
  products: []
};

const appReducer = createReducer(
  initialState,
  on(appActions.setUser, (state, { user }) => ({ ...state, user })),
  on(appActions.addProduct, (state, { product }) => {
    const products = [...state.products, product];
    return { ...state, products };
  }),
  on(appActions.removeProduct, (state, { id }) => {
    const products = state.products.filter(product => product.id !== id);
    return { ...state, products };
  })
);

export function reducer(state: AppState | undefined, action: Action) {
  return appReducer(state, action);
}

在上述代码中,我们首先定义了一个初始状态initialState,它包含了我们应用程序的默认状态。

然后,我们使用createReducer函数创建了一个名为appReducer的Reducer。createReducer函数接受初始状态和一系列的Reducer函数作为参数,并返回一个新的Reducer函数。

在我们的示例中,我们创建了三个Reducer函数,分别对应着我们的三个Actions。每个Reducer函数都接受当前状态和Action作为输入,并返回一个新的状态。

最后,我们通过导出reducer函数来使它可以在应用程序中使用。

创建Selectors

Selectors用于从状态中选择和提取数据。我们可以使用Selectors来获取符合特定条件的数据,并将其供给组件使用。

store文件夹中,我们将创建一个名为app.selectors.ts的文件,并在其中定义我们的应用程序Selectors。

// src/app/store/app.selectors.ts

import { createSelector, createFeatureSelector } from '@ngrx/store';
import { AppState, User, Product } from '../models';

// 获取用户
export const selectUser = (state: AppState) => state.user;

// 获取产品列表
export const selectProducts = (state: AppState) => state.products;

// 获取特定ID的产品
export const selectProductById = (id: number) =>
  createSelector(
    selectProducts,
    (products: Product[]) => products.find(product => product.id === id)
  );

// 创建一个特定用户的Selector
export const selectUserProducts = createSelector(
  selectUser,
  selectProducts,
  (user: User, products: Product[]) => products.filter(product => product.userId === user.id)
);

在上述代码中,我们使用createSelector函数来创建我们的Selectors。createSelector函数接受一系列的输入Selectors和一个转换函数作为参数,并返回一个新的Selector。

我们的示例中,我们创建了四个Selectors,分别用于获取用户信息、产品列表、特定ID的产品以及特定用户的产品列表。

创建Effects

NgRx Effects用于处理副作用和异步操作。我们可以使用Effects来处理异步数据获取、更新本地存储、调用API等操作。

store文件夹中,我们将创建一个名为app.effects.ts的文件,并在其中定义我们的应用程序Effects。

// src/app/store/app.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 appActions from './app.actions';
import { ProductService } from '../services/product.service';

@Injectable()
export class AppEffects {
  loadProducts$ = createEffect(() =>
    this.actions$.pipe(
      ofType(appActions.loadProducts),
      mergeMap(() =>
        this.productService.getProducts().pipe(
          map(products => appActions.setProducts({ products })),
          catchError(error => of(appActions.loadProductsFailure({ error })))
        )
      )
    )
  );

  constructor(
    private actions$: Actions,
    private productService: ProductService
  ) {}
}

在上述代码中,我们首先使用@Injectable装饰器来修饰AppEffects类,并将其导出。

然后,我们通过createEffect函数创建了一个名为loadProducts$的Effect。createEffect函数接受一个Observable作为输入,并返回一个新的Observable,用于捕获并处理Actions。

在我们的示例中,我们捕获了一个名为loadProducts的Action,并通过mergeMap运算符将其转化为一个异步操作。在异步操作中,我们调用了productService.getProducts()来获取产品列表,并使用map运算符将其转化为一个setProducts的Action,用于更新状态。在异步操作中,我们还可以使用catchError运算符来处理错误。

最后,我们使用constructor函数来实例化依赖对象,并将它们注入到当前类中。

注册模块

现在,我们需要在Angular应用程序中注册我们的状态模块。

打开src/app文件夹,创建一个名为store.module.ts的文件,并在其中注册我们的状态模块。

// src/app/store/store.module.ts

import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { reducer } from './app.reducer';
import { AppEffects } from './app.effects';

@NgModule({
  imports: [
    StoreModule.forRoot({ app: reducer }),
    EffectsModule.forRoot([AppEffects])
  ]
})
export class StoreModule {}

在上述代码中,我们首先从@angular/core@ngrx/store中导入NgModuleStoreModule装饰器。

然后,我们定义了一个用于注册我们的状态模块的类,并使用@NgModule装饰器来修饰它。

imports数组中,我们使用StoreModule.forRoot({ app: reducer })来注册我们的状态模块。forRoot方法接受一个状态和一个或多个插件,并返回一个Angular模块。

最后,我们使用EffectsModule.forRoot([AppEffects])来注册我们的Effects模块。EffectsModule.forRoot方法接受一个包含Effects类的数组,并返回一个Angular模块。

使用状态

现在,我们可以在我们的Angular组件中使用我们的状态了。

打开一个组件文件,导入Store和我们的Actions和Selectors。

import { Store } from '@ngrx/store';
import * as appActions from '../store/app.actions';
import { selectUser, selectProducts, selectUserProducts } from '../store/app.selectors';

在组件的构造函数中,注入Store

constructor(private store: Store) {}

在组件中,你可以使用store.dispatch方法来派发Actions。

// 设置用户
this.store.dispatch(appActions.setUser({ user }));

// 添加产品
this.store.dispatch(appActions.addProduct({ product }));

// 删除产品
this.store.dispatch(appActions.removeProduct({ id }));

你也可以使用store.select方法来选择和提取状态。

// 获取用户
this.store.select(selectUser).subscribe(user => {
  console.log(user);
});

// 获取产品列表
this.store.select(selectProducts).subscribe(products => {
  console.log(products);
});

// 获取特定ID的产品
this.store.select(selectProductById(1)).subscribe(product => {
  console.log(product);
});

// 获取特定用户的产品列表
this.store.select(selectUserProducts).subscribe(products => {
  console.log(products);
});

总结

利用Angular和NgRx进行状态管理可以帮助我们更好地组织和管理应用程序中的数据。在本篇文章中,我们了解了如何使用Angular和NgRx进行状态管理,并通过Actions、Reducers、Selectors和Effects来处理状态的变化和副作用。

使用Angular和NgRx进行状态管理可以提高应用程序的性能和可维护性,使其更容易理解和扩展。希望本篇文章能够对你在使用Angular和NgRx进行状态管理时有所帮助。


全部评论: 0

    我有话说: