1、ngrx/store是什么
ngrx/store是Angular的状态管理库,基于Redux模式构建,且它必须和rxjs一起使用,因为它使用rxjs管理状态,并且可以使用rxjs的操作符和工具来处理状态数据流。
2、主要文件构成
-
state.ts
- 使用interface、enum、class定义所有状态
-
action.ts
- 使用createAction(p1,p2?)方法创建action,action本质是一个对象,所有状态的更改都需要通过派发
(dispatch)action的形式。 - p1是一个字符串,用来定义action的type,必须使用字符串来标识action,避免手写字符串时发生错误,也方便未来的维护和修改。
- p2是一个函数,接收一个对象参数。
- 使用createAction(p1,p2?)方法创建action,action本质是一个对象,所有状态的更改都需要通过派发
-
reducer.ts
- 使用
createReducer(initState,...ons)方法创建reducer,用于监听action,更新state。 - initState,一个对象,指定了store的初始状态。
- on方法是action的处理函数,每个action对应一个处理函数。
- 使用
-
effect.ts
- 用于发起请求
-
...module.ts
- 定义完store后需要在模块中进行注册,如果是多个store的话,可以在app.module.ts中进行全局注册,如果是单个store,可以只在对应模块下注册。
- 单个模块注册:
- 使用
StoreModule.forFeature(featureName: string, reducers: ActionReducerMap<any>, config?: StoreConfig<any>)方法,它的作用是将指定的Feature模块的Store注册到应用的Store中。 featureName: string是 Feature Module 的名称,在 Store 的状态树上会以该名称作为键值,使用多个 Feature Module 时,它可以让我们快速找到对应的状态树。- 第二个参数可以是一个包含一系列reducer的对象,也可以是一个InjectionToken。如果使用的是InjectionToken,它应该指向一个ActionReducerMap对象,它包括被注册的Feature模块的所有reducers。ActionReducerMap对象可以由工厂函数生成。
imports: [StoreModule.forFeature(USER_MANAGE, USER_MANAGE_TOKEN)] providers: [ { provide: USER_MANAGE_TOKEN, useFactory: getUserManageReducers, //useFactory(getUserManageReducers)则是一个工厂函数,用于生成一个特定的类型实例。 //在这里,它用于生成一个 ActionReducerMap 类型的实例,这个实例包含了一组指定的 reducer。 //也就是说,在依赖注入时会调用 getUserManageReducers 方法,生成一个指定的对象, //并将该对象作为 USER_MANAGE_TOKEN 的依赖项值传递给应用中的所有组件。 } ]//getUserManageReducers函数如下 export function getUserManageReducers() { return { [UserManageName.USER_ANALYSIS]: UserAnalysisReducer.userAnalysisReducer }; } //其中UserManageName.USER_ANALYSIS是定义的一个常量。 //UserAnalysisReducer.userAnalysisReducer是一个由createReducer方法创建的reducer。 - 使用
- 全局注册:
- 使用
StoreModule.forRoot(reducers,config?)方法, - reducers: ActionReducerMap<any>参数是一个对象。这个参数的值是一个 ActionReducerMap 类型的变量,它定义了一个对象,该对象的每个属性都对应一个 reducer 函数。
- 使用
- config?: StoreConfig<any>,该参数是一个可选参数,用于配置 Store。它包含以下几个属性:
initialState表示 Store 的初始状态,默认为{};runtimeChecks表示运行时检查的数组,用于配置要执行哪些运行时检查,默认为{}。
-
使用store
- 首先要使用select方法获取状态源,这个状态源对应的就是我们在模块中注册的reducer。
- 可以使用链式的方式获取某个状态,比如
this.store.pipe( select(UserManageName.USER_MANAGE), //select的参数要和module.ts文件中的StoreModule.forFeature函数的第一个参数一致。 //查询到的其实是一个reducer。即用createReducer创建的。 //换句话说,我们的store其实就是一个reducer。 //此处我们查到UserManageName.USER_MANAGE对应了一个USER_MANAGE_TOKEN, //而USER_MANAGE对应了一个USER_MANAGE_TOKEN是由getUserManageReducers函数生成的reducer集合。 //所以在此处的第一个查询,查到的就是getUserManageReducers函数的返回值。即{user_analysis: {…}}。 select(UserManageName.USER_ANALYSIS),//查询到的是{user_analysis: {…}}中的user_analysis对象 select('userAnalysisListRequest'),//user_analysis对象中的一个属性 select('pageSize')//userAnalysisListRequest下的一个属性 )//如果不想每次都先查store源,再查下面的属性。 //可以统一查询一次源store。以后的每个属性,在这个的基础上再继续查即可。 cstHomeListStore: Observable<cstCustomerHomeListState.CstCustomerHomeListState> = this.store.pipe(select(cstCustomerManage.cstHomeList));//在此处查询一次store源。 //在下面2个地方直接使用this.cstHomeListStore.pipe即可。 //effectivePeriod effectivePeriod: Observable<number> = this.cstHomeListStore.pipe( select(cstCustomerHomeListState.cstCustomerHomeListStateEnum.listRequest), select(cstCustomerHomeListState.listRequestEnum.effective), ); //enterpeiseInfo enterpeiseInfo: Observable<number> = this.cstHomeListStore.pipe( select(cstCustomerHomeListState.cstCustomerHomeListStateEnum.listRequest), select(cstCustomerHomeListState.listRequestEnum.companyInfo), );下面是3个项目中实际用到的state、action、reducer的文件,供学习。
//state.ts /**学员增长分析列表 */ export enum userAnalysisListEnum { date = 'date', registerUser = 'registerUser', totalUser = 'totalUser',//累计学员数 pcWeb = 'pcWeb',//PC网页 wechatWeb = 'wechatWeb', //微信网页 mobileH5 = 'mobileH5', //手机H5 app = 'app',//APP miniProgram = 'miniProgram',//小程序 agentChannel = 'agentChannel',//代理渠道 softwareUser = 'softwareUser', //软件用户 } export interface UserAnalysisList { [userAnalysisListEnum.date]:string; [userAnalysisListEnum.registerUser]:number; [userAnalysisListEnum.agentChannel]:number; [userAnalysisListEnum.app]:number; [userAnalysisListEnum.mobileH5]:number; [userAnalysisListEnum.miniProgram]:number; [userAnalysisListEnum.pcWeb]:number; [userAnalysisListEnum.softwareUser]:number; [userAnalysisListEnum.totalUser]:number; [userAnalysisListEnum.wechatWeb]:number; } /** 新增用户request */ export class UserAnalysisListRequest { pageIndex = 1; pageSize = 30; timeFrom: number; timeTo: number; } /** * 接口返回 */ export interface Result<T> { code: number; data: T; detailMsg: string; msg: string; succeed: boolean; } export interface ResultData<T> { count: number; data: any; dataAmount: number; index: number; pageAmount: number; rows: T; } /** * 总store */ export interface UserAnalysisState { tabIndex: number; userAnalysisListRequest: UserAnalysisListRequest; tableLoading:boolean; userAnalysisList: UserAnalysisList[]; }//action.ts import {createAction, props} from '@ngrx/store'; import * as UserAnalysisState from './state'; export const CHANGETABINDEX = '[user manage] [user analysis] change tabindex'; /** * 改变用户列表 */ export const CHANGEUSERLIST = '[user manage] [user analysis] change user list'; /** * 改变totalPage */ export const CHANGETOTALPAGE = '[user manage] [user analysis] change total page'; /** * 改变pageIndex */ export const CHANGEPAGEINDEX = '[user manage] [user analysis] change page index'; /** * 改变pageSize */ export const CHANGEPAGESIZE = '[user manage] [user analysis] change page size'; /** * 改timeFrom */ export const CHANGETIMEFROM = '[user manage] [user analysis] change time from'; /** * 改变timeTo */ export const CHANGETIMETO = '[user manage] [user analysis] change time to'; /** * 改变列表loading */ export const CHANGETABLELOADING = '[user manage] [user analysis] change table loading'; /** * 修改时间 */ export const CHANGERANGEDATE = '[user manage] [user analysis] change range date'; export const changeTabIndex = createAction(CHANGETABINDEX, props<{tabIndex: number}>()); export const changeUserList = createAction(CHANGEUSERLIST,props<{list: UserAnalysisState.UserAnalysisList[]}>()); export const changeTotalPage = createAction(CHANGETOTALPAGE, props<{totalPage: number}>()); export const changePageIndex = createAction(CHANGEPAGEINDEX, props<{pageIndex: number}>()); export const changePageSize = createAction(CHANGEPAGESIZE, props<{pageSize: number}>()); export const changeTimeFrom = createAction(CHANGETIMEFROM, props<{timeFrom: number}>()); export const changeTimeTo = createAction(CHANGETIMETO, props<{timeTo: number}>()); export const changeTableLoading = createAction(CHANGETABLELOADING, props<{isLoading: boolean}>()); export const changeRangeDate = createAction(CHANGERANGEDATE,props<{rangeDate:Date|null[]}>())//reducer.ts import * as UserAnalysisAction from './action'; import * as UserAnalysisState from './state'; import {Action, ActionReducer, createReducer, on} from '@ngrx/store'; const newUserRequest: UserAnalysisState.UserAnalysisListRequest = new UserAnalysisState.UserAnalysisListRequest(); const userAnalysisInitState: UserAnalysisState.UserAnalysisState = { userAnalysisListRequest: {...newUserRequest}, tableLoading: false, userAnalysisList: [], tabIndex: 0, }; const reducer: ActionReducer<UserAnalysisState.UserAnalysisState | undefined, Action> = createReducer( userAnalysisInitState, on(UserAnalysisAction.changeTabIndex, (state, action): UserAnalysisState.UserAnalysisState => ({...state, tabIndex: action.tabIndex})), on(UserAnalysisAction.changePageIndex, (state, action): UserAnalysisState.UserAnalysisState => ( {...state, userAnalysisListRequest: {...state.userAnalysisListRequest, pageIndex: action.pageIndex} } )), ); export function userAnalysisReducer(state: UserAnalysisState.UserAnalysisState | undefined, action: Action) : UserAnalysisState.UserAnalysisState { return reducer(state, action); }//componnet.ts import { Component, OnInit } from '@angular/core'; import {select, Store} from '@ngrx/store'; import {UserManageState} from '../store/reg-token'; import {Observable} from 'rxjs'; import * as UserManageName from '../store'; import * as UserAnalysisState from '../store/user-analysis/new/state'; import * as UserAnalysisAction from '../store/user-analysis/new/action'; import {PageOptions} from '../../../entity/pollen'; import {ActivatedRoute,Router} from '@angular/router' @Component({ selector: 'app-user-analysis', templateUrl: './user-analysis.component.html', styleUrls: ['./user-analysis.component.scss'] }) export class UserAnalysisComponent implements OnInit { dateRange = [new Date().setDate(1), new Date()]; selectTime=0; pageSizeOptions = PageOptions; tabIndex: Observable<number> = this.store.pipe( select(UserManageName.USER_MANAGE), select(UserManageName.USER_ANALYSIS), select('tabIndex') );//tabIndex被定义为一个可观察对象 //UserManageName.USER_MANAGE模块是在reg-token里面定义的 tableLoading:Observable<boolean>= this.store.pipe( select(UserManageName.USER_MANAGE),select(UserManageName.USER_ANALYSIS),select('tableLoading') ); request:Observable<any>= this.store.pipe( select(UserManageName.USER_MANAGE),select(UserManageName.USER_ANALYSIS),select('userAnalysisListRequest') ); pageIndex:Observable<number>= this.store.pipe( select(UserManageName.USER_MANAGE),select(UserManageName.USER_ANALYSIS),select('userAnalysisListRequest'), select('pageIndex') ); tableList:Observable<Array<UserAnalysisState.UserAnalysisList>>= this.store.pipe( select(UserManageName.USER_MANAGE),select(UserManageName.USER_ANALYSIS),select('tableList') ) pageSize:Observable<number>= this.store.pipe( select(UserManageName.USER_MANAGE),//select的参数要和module.ts文件中的StoreModule.forFeature函数的第一个参数一致。 //查询到的其实是一个reducer。即用createReducer创建的。换句话说,我们的store其实就是一个reducer。 //在这里查询到的是一个对象:{user_analysis: {…}}。这个对象是getUserManageReducers函数的返回值。 select(UserManageName.USER_ANALYSIS),//查询到的是reducer函数返回的state对象 select('userAnalysisListRequest'), select('pageSize') ) constructor( private store: Store<any> ) { } ngOnInit() { this.store.pipe( select(UserManageName.USER_MANAGE), ).subscribe(res=>{ console.log(res) }) this.pageIndex.subscribe(res=>{ console.log('pageIndex',res) }) this.pageSize.subscribe(res=>{ console.log('pageSize',res) }) } changeTabIndex(tabIndex: number): void { this.selectTime = tabIndex == 0 ? 0 :5; this.store.dispatch(UserAnalysisAction.changeTabIndex({tabIndex})); } }