Angular数据状态管理框架:NgRx/Store

简介: ngrx/store 是基于RxJS的状态管理库,其灵感来源于Redux。在NgRx中,状态是由一个包含action和reducer的函数的映射组成的。Reducer函数经由action的分发以及当前或初始的状态而被调用,最后由reducer返回一个不可变的状态。

ngrx/store 是基于RxJS的状态管理库,其灵感来源于Redux。在NgRx中,状态是由一个包含actionreducer的函数的映射组成的。Reducer函数经由action的分发以及当前或初始的状态而被调用,最后由reducer返回一个不可变的状态。

image.png

状态管理

在前端大型复杂Angular/AngularJS项目的状态管理一直是个让人头疼的问题。在AngularJS(1.x版本)中,状态管理通常由服务,事件,$rootScope混合处理。在Angular中(2+版本),组件通信让状态管理变得清晰一些,但还是有点复杂,根据数据流向不同会用到很多方法。

ngrx/store中的基本原则

视图层通过dispatch发起一个行为(action)、Reducer接收action,根据action.type类型来判断执行、改变状态、返回一个新的状态给store、由store更新state

image.png

  • State(状态) 是状态(state)存储器
  • Action(行为) 描述状态的变化
  • Reducer(归约器/归约函数) 根据先前状态以及当前行为来计算出新的状态,里面的方法为纯函数
  • 状态用State的可观察对象,Action的观察者——Store来访问

Actions(行为)

Actions是信息的载体,它发送数据到reducer,然后reducer更新storeActionsstore能接受数据的唯一方式。

ngrx/store里,Action的接口是这样的:

// actions包括行为类型和对应的数据载体
export interface Action {
  type: string;
  payload?: any;
}

type描述期待的状态变化类型。比如,添加待办 ADD_TODO,增加 DECREMENT 等。payload是发送到待更新store中的数据。store派发action 的代码类似如下:

// 派发action,从而更新store
store.dispatch({
  type: 'ADD_TODO',
  payload: 'Buy milk'
});

Reducers(归约器)

Reducers规定了行为对应的具体状态变化。是纯函数,通过接收前一个状态和派发行为返回新对象作为下一个状态的方式来改变状态,新对象通常用Object.assign和扩展语法来实现。

// reducer定义了action被派发时state的具体改变方式
export const todoReducer = (state = [], action) => {
  switch(action.type) {
    case 'ADD_TODO':
      return [...state, action.payload];
    default:
      return state;
  }
}

开发时特别要注意函数的纯性。因为纯函数:

  • 不会改变它作用域外的状态
  • 输出只决定于输入
  • 相同输入,总是得到相同输出

Store(存储)

store中储存了应用中所有的不可变状态。ngrx/store中的storeRxJS状态的可观察对象,以及行为的观察者。

可以利用Store来派发行为。也可以用Store的select()方法获取可观察对象,然后订阅观察,在状态变化之后做出反应。

上面我们描述的是基本流程。在实际开发过程中,会涉及API请求、浏览器存储等异步操作,就需要effectsserviceseffectsaction触发,进行一些列逻辑后发出一个或者多个需要添加到队列的action,再由reducers处理。

image.png

使用ngrx/store框架开发应用,始终只维护一个状态,并减少对API的调用。

简单示例

简单介绍一个管理系统的登录模块。

创建Form表单

  1. 增加组件:LoginComponent,主要就是布局,代码为组件逻辑
  2. 定义用户:User Model
export class User {
    id: number;
    username: string;
    password: string;
    email: string;
    avatar: string;
    clear(): void {
        this.id = undefined;
        this.username = "";
        this.password = "";
        this.email = "";
        this.avatar = "./assets/default.jpg";
    }
}
  1. 添加表单:在组件LoginComponent增加Form表单

NGRX Store

按照上述的4个原则定义相应的Actions

image.png

  • reducers定义状态
    在文件auth.reducers.ts中创建状态,并初始化
export interface AuthState {
    isAuthenticated: boolean;
    user: User | null;
    errorMessage: string | null;
}
export const initialAuthState: AuthState = {
    isAuthenticated: false,
    user: null,
    errorMessage: null
};
  • actions定义行为
export enum AuthActionTypes {
    Login = "[Auth] Login",
    LoginSuccess = "[Auth] Login Success",
    LoginFailure = "[Auth] Login Failure"
}
export class Login implements Action {
    readonly type = AuthActionTypes.Login;
    constructor(public payload: any) {}
}
  • service实现数据交互(服务器)
@Injectable()
export class AuthService {
    private BASE_URL = "api/user";
    constructor(private http: HttpClient) {}
    getToken(): string {
        return localStorage.getItem("token");
    }
    login(email: string, pwd: string): Observable<any> {
        const url = `${this.BASE_URL}/login`;
        return this.http.post<User>(url, { email, pwd });
    }
}
  • effects侦听从Store调度的动作,执行某些逻辑,然后分派新动作
  • 一般情况下只在这里调用API
  • 通过返回一个action给reducer进行操作来改变store的状态
  • effects总是返回一个或多个action(除非@Effect with {dispatch: false})
  • image.png
@Effect()
Login: Observable<any> = this.actions.pipe(
    ofType(AuthActionTypes.Login),   //执行Login响应
    map((action: Login) => action.payload),
    switchMap(payload => {
        return this.authService.login(payload.email, payload.password).pipe(
            map(user => {
                return new LoginSuccess({ uid: user.id, email: payload.email });
            }),
            catchError(error => {
                return of(new LoginFailure(error));
            })
        );
    })
);
//失败的效果
@Effect({ dispatch: false })
LoginFailure: Observable<any> = this.actions.pipe(ofType(AuthActionTypes.LoginFailure));
//成功的效果
@Effect({ dispatch: false })
LoginSuccess: Observable<any> = this.actions.pipe(
    ofType(AuthActionTypes.LoginSuccess),
    tap(user => {
        localStorage.setItem("uid", user.payload.id);
        this.router.navigateByUrl("/sample");
    })
);


相关文章
|
3月前
|
前端开发 JavaScript 开发者
什么是 Angular 框架中的 Zone.js
什么是 Angular 框架中的 Zone.js
26 0
|
7月前
|
存储 API 网络架构
Angular Ngrx Store 应用程序状态的一些典型例子
Angular Ngrx Store 应用程序状态的一些典型例子
36 0
|
7月前
|
存储 JavaScript 前端开发
Angular Ngrx Store 里 State 和 Reducer 的绑定关系是如何实现的
Angular Ngrx Store 里 State 和 Reducer 的绑定关系是如何实现的
56 0
|
1月前
|
开发框架 移动开发 JavaScript
探索前端开发框架:React、Angular 和 Vue 的对决(四)
探索前端开发框架:React、Angular 和 Vue 的对决(四)
|
1月前
|
开发框架 JavaScript 前端开发
探索前端开发框架:React、Angular 和 Vue 的对决(三)
探索前端开发框架:React、Angular 和 Vue 的对决(三)
|
1月前
|
开发框架 前端开发 JavaScript
探索前端开发框架:React、Angular 和 Vue 的对决(二)
探索前端开发框架:React、Angular 和 Vue 的对决(二)
|
1月前
|
开发框架 前端开发 JavaScript
探索前端开发框架:React、Angular 和 Vue 的对决(一)
探索前端开发框架:React、Angular 和 Vue 的对决(一)
|
4月前
|
JavaScript 前端开发 开发工具
Angular框架
Angular框架
37 0
|
5月前
|
JavaScript UED SEO
Angular 应用启用服务器端渲染后 Ngrx store 和 re-hydration 的交互关系
Angular 应用启用服务器端渲染后 Ngrx store 和 re-hydration 的交互关系
30 1
|
5月前
|
JavaScript 前端开发 开发工具
TypeScript 可以与其他流行的开发工具和框架结合使用,如 Angular 和 React 等。具体应用案例解析
TypeScript 可以与其他流行的开发工具和框架结合使用,如 Angular 和 React 等。具体应用案例解析

热门文章

最新文章