前言
@ngrx/store 作为 Angular 的状态管理库,在实现上必然离不开依赖注入,所以不清楚依赖注入概念的请先读这篇文章了解下。
正文1 - 依赖注入
StoreModule.forRoot()
static forRoot(reducers:| ActionReducerMap<any, any>| InjectionToken<ActionReducerMap<any, any>>,config: RootStoreConfig<any, any> = {}): ModuleWithProviders<StoreRootModule> {return {ngModule: StoreRootModule,providers: [// A{provide: _ROOT_STORE_GUARD,useFactory: _provideForRootGuard,deps: [[Store, new Optional(), new SkipSelf()]],},// B1{ provide: _INITIAL_STATE, useValue: config.initialState },// B2{provide: INITIAL_STATE,useFactory: _initialStateFactory,deps: [_INITIAL_STATE],},// C1{ provide: _INITIAL_REDUCERS, useValue: reducers },// C2{provide: _STORE_REDUCERS,useExisting:reducers instanceof InjectionToken ? reducers : _INITIAL_REDUCERS,},// C3{provide: INITIAL_REDUCERS,deps: [Injector, _INITIAL_REDUCERS, [new Inject(_STORE_REDUCERS)]],useFactory: _createStoreReducers,},// D1{provide: USER_PROVIDED_META_REDUCERS,useValue: config.metaReducers ? config.metaReducers : [],},// D2{provide: _RESOLVED_META_REDUCERS,deps: [META_REDUCERS, USER_PROVIDED_META_REDUCERS],useFactory: _concatMetaReducers,},// D3{provide: _REDUCER_FACTORY,useValue: config.reducerFactory? config.reducerFactory: combineReducers,},// D4{provide: REDUCER_FACTORY,deps: [_REDUCER_FACTORY, _RESOLVED_META_REDUCERS],useFactory: createReducerFactory,},// JACTIONS_SUBJECT_PROVIDERS,// KREDUCER_MANAGER_PROVIDERS,// LSCANNED_ACTIONS_SUBJECT_PROVIDERS,// MSTATE_PROVIDERS,// NSTORE_PROVIDERS,// OprovideRuntimeChecks(config.runtimeChecks),// PcheckForActionTypeUniqueness(),],};}
1、reducers 参数类型还可以是 InjectionToken 类型,这样就提供了一种可以以依赖注入的方式来初始化 reducers
2、该方法注册了很多提供者,这里先重点看 B1,B2,C1,C2,C3,D1,D2,D3,D4,J,K,L,M,N 这些提供者
- B1,B2
B1=config.initialState
B2= _initialStateFactory(B1)
export function _initialStateFactory(initialState: any): any {if (typeof initialState === 'function') {return initialState();}return initialState;}
B2只是做了个兼容,这里也可以看出config.initialState可以是个函数,用B2要保险些。搞了这么多,实际上都是参数处理过程,使用的时候用B2就完事了
- C1,C2,C3
C1 = config.reducers
C2,如果config.reducers 是 InjectionToken 类型,就取config.reducers,否则取C1,其实C2和C1差不多,就是想用C2强调reducers还可以是InjectionToken类型,注意这里用的是 useExisting
C3 = _createStoreReducers(injector,reducers),C3 声明了3个依赖,其实现却只用了前两个依赖,实际上第二个依赖和第三个依赖是回事
{provide: INITIAL_REDUCERS,deps: [Injector, _INITIAL_REDUCERS, [new Inject(_STORE_REDUCERS)]],useFactory: _createStoreReducers,}export function _createStoreReducers(injector: Injector,reducers: ActionReducerMap<any, any>) {return reducers instanceof InjectionToken ? injector.get(reducers) : reducers;}
搞了这么多,实际上都是参数处理过程,使用的时候用C3就完事了
- D1,D2,D3,D4
和前面类似,都是处理参数,这次是 config.metaReducers 和 config.reducerFactory
D1 = config.metaReducers || []
D2 = _concatMetaReducers(META_REDUCERS,D1)
export function _concatMetaReducers(metaReducers: MetaReducer[],userProvidedMetaReducers: MetaReducer[]): MetaReducer[] {return metaReducers.concat(userProvidedMetaReducers);}
D3 = config.reducerFactory??combineReducers
D4 = createReducerFactory(D3,D2)
- J
一个 BehaviorSubject 的子类,用于传递 action,称为 action 流
export const INIT = '@ngrx/store/init' as '@ngrx/store/init';@Injectable()export class ActionsSubject extends BehaviorSubject<Action>implements OnDestroy {constructor() {super({ type: INIT });}next(action: Action): void {super.next(action);}complete() {/* noop */}ngOnDestroy() {super.complete();}}
- K
这里就注入了 B2,C3,D4,是一个核心函数,用于 reducer 管理
@Injectable()export class ReducerManager extends BehaviorSubject<ActionReducer<any, any>>implements OnDestroy {constructor(private dispatcher: ReducerManagerDispatcher,@Inject(INITIAL_STATE) private initialState: any,@Inject(INITIAL_REDUCERS) private reducers: ActionReducerMap<any, any>,@Inject(REDUCER_FACTORY)private reducerFactory: ActionReducerFactory<any, any>) {super(reducerFactory(reducers, initialState));}addFeature(feature: StoreFeature<any, any>) {this.addFeatures([feature]);}addFeatures(features: StoreFeature<any, any>[]) {const reducers = features.reduce((reducerDict,{ reducers, reducerFactory, metaReducers, initialState, key }) => {const reducer =typeof reducers === 'function'? createFeatureReducerFactory(metaReducers)(reducers, initialState): createReducerFactory(reducerFactory, metaReducers)(reducers,initialState);reducerDict[key] = reducer;return reducerDict;},{} as { [key: string]: ActionReducer<any, any> });this.addReducers(reducers);}removeFeature(feature: StoreFeature<any, any>) {this.removeFeatures([feature]);}removeFeatures(features: StoreFeature<any, any>[]) {this.removeReducers(features.map((p) => p.key));}addReducer(key: string, reducer: ActionReducer<any, any>) {this.addReducers({ [key]: reducer });}addReducers(reducers: { [key: string]: ActionReducer<any, any> }) {this.reducers = { ...this.reducers, ...reducers };this.updateReducers(Object.keys(reducers));}removeReducer(featureKey: string) {this.removeReducers([featureKey]);}removeReducers(featureKeys: string[]) {featureKeys.forEach((key) => {this.reducers = omit(this.reducers, key) /*TODO(#823)*/ as any;});this.updateReducers(featureKeys);}private updateReducers(featureKeys: string[]) {this.next(this.reducerFactory(this.reducers, this.initialState));this.dispatcher.next(<Action>{type: UPDATE,features: featureKeys,});}ngOnDestroy() {this.complete();}}
- L
ScannedActionsSubject 目前啥都没做,只是一个 Subject 的子类,后续主要用于 ngrx/store 功能的扩展,@store/effect 就用到了这个
@Injectable()export class ScannedActionsSubject extends Subject<Action>implements OnDestroy {ngOnDestroy() {this.complete();}}
- M
state 流
@Injectable()export class State<T> extends BehaviorSubject<any> implements OnDestroy {static readonly INIT = INIT;private stateSubscription: Subscription;constructor(actions$: ActionsSubject,reducer$: ReducerObservable,scannedActions: ScannedActionsSubject,@Inject(INITIAL_STATE) initialState: any) {super(initialState);const actionsOnQueue$: Observable<Action> = actions$.pipe(observeOn(queueScheduler));const withLatestReducer$: Observable<[Action,ActionReducer<any, Action>]> = actionsOnQueue$.pipe(withLatestFrom(reducer$));const seed: StateActionPair<T> = { state: initialState };const stateAndAction$: Observable<{state: any;action?: Action;}> = withLatestReducer$.pipe(scan<[Action, ActionReducer<T, Action>], StateActionPair<T>>(reduceState,seed));this.stateSubscription = stateAndAction$.subscribe(({ state, action }) => {this.next(state);scannedActions.next(action!);});}ngOnDestroy() {this.stateSubscription.unsubscribe();this.complete();}}
- N
store
@Injectable()export class Store<T = object> extends Observable<T>implements Observer<Action> {constructor(state$: StateObservable,private actionsObserver: ActionsSubject,private reducerManager: ReducerManager) {super();this.source = state$;}select<Props = any, K = any>(pathOrMapFn: ((state: T, props?: Props) => K) | string,...paths: string[]): Observable<any> {return (select as any).call(null, pathOrMapFn, ...paths)(this);}lift<R>(operator: Operator<T, R>): Store<R> {const store = new Store<R>(this, this.actionsObserver, this.reducerManager);store.operator = operator;return store;}dispatch<V extends Action = Action>(action: V &FunctionIsNotAllowed<V,'Functions are not allowed to be dispatched. Did you forget to call the action creator function?'>) {this.actionsObserver.next(action);}next(action: Action) {this.actionsObserver.next(action);}error(err: any) {this.actionsObserver.error(err);}complete() {this.actionsObserver.complete();}addReducer<State, Actions extends Action = Action>(key: string,reducer: ActionReducer<State, Actions>) {this.reducerManager.addReducer(key, reducer);}removeReducer<Key extends Extract<keyof T, string>>(key: Key) {this.reducerManager.removeReducer(key);}}
到这里为止,ngrx/store 里的一些关键函数都得到了注册。
正文2 - StoreRootModule
下面看调用过程:forRoot 方法除了返回一堆 provider 外,还返回了 StoreRootModule,StoreRootModule会被 Angular 初始化,从而其constructor中那些依赖会被注入,也即上面的 provider 会被初始化。
@NgModule({})export class StoreRootModule {constructor(actions$: ActionsSubject,reducer$: ReducerObservable,scannedActions$: ScannedActionsSubject,store: Store<any>,@Optional()@Inject(_ROOT_STORE_GUARD)guard: any,@Optional()@Inject(_ACTION_TYPE_UNIQUENESS_CHECK)actionCheck: any) {}}
- Store
Store 有三个依赖:state流,action流和 reducerManager,有 dispatch 和 select 两个常用方法,dispatch 实际上就是调用 action流的next 方法让 action 向下流动,这里主要看下 select 方法
select<Props = any, K = any>(pathOrMapFn: ((state: T, props?: Props) => K) | string,...paths: string[]): Observable<any> {return (select as any).call(null, pathOrMapFn, ...paths)(this);}export function select<T, Props, K>(pathOrMapFn: ((state: T, props?: Props) => any) | string,propsOrPath?: Props | string,...paths: string[]) {return function selectOperator(source$: Observable<T>): Observable<K> {let mapped$: Observable<any>;if (typeof pathOrMapFn === 'string') {const pathSlices = [<string>propsOrPath, ...paths].filter(Boolean);mapped$ = source$.pipe(pluck(pathOrMapFn, ...pathSlices));} else if (typeof pathOrMapFn === 'function') {mapped$ = source$.pipe(map(source => pathOrMapFn(source, <Props>propsOrPath)));} else {throw new TypeError(`Unexpected type '${typeof pathOrMapFn}' in select operator,` +` expected 'string' or 'function'`);}return mapped$.pipe(distinctUntilChanged());};}
注意 select 函数返回的最终值是 source$.pipe(…).pipe(distinctUntilChanged()),而source$ 就是依赖的 state 流。所以只要 state 流有新值产生,source$.pipe(…).pipe(distinctUntilChanged())的观察者们就会执行一遍,这也是为什么在不同的地方调用 this.store.select(‘xxx’).subcribe(callback) 后,不论在哪里执行 dispatch,callback 都有机会执行(distinctUntilChanged的存在导致callback不一定执行)
- state 流
constructor(actions$: ActionsSubject,reducer$: ReducerObservable,scannedActions: ScannedActionsSubject,@Inject(INITIAL_STATE) initialState: any) {super(initialState);const actionsOnQueue$: Observable<Action> = actions$.pipe(observeOn(queueScheduler));const withLatestReducer$: Observable<[Action,ActionReducer<any, Action>]> = actionsOnQueue$.pipe(withLatestFrom(reducer$));const seed: StateActionPair<T> = { state: initialState };const stateAndAction$: Observable<{state: any;action?: Action;}> = withLatestReducer$.pipe(scan<[Action, ActionReducer<T, Action>], StateActionPair<T>>(reduceState,seed));this.stateSubscription = stateAndAction$.subscribe(({ state, action }) => {this.next(state);scannedActions.next(action!);});}
1、其本身是一个BehaviorSubject,产出 state
2、订阅了 action 流的变化
3、使用 scan + reduceState来迭代 state 状态
export function reduceState<T, V extends Action = Action>(stateActionPair: StateActionPair<T, V> = { state: undefined },[action, reducer]: [V, ActionReducer<T, V>]): StateActionPair<T, V> {const { state } = stateActionPair;return { state: reducer(state, action), action };}
从这儿可以看出,每当有action触发,都会进入 reduceState, 产生一个新 state,组成 {state,action}对传给订阅者
this.stateSubscription = stateAndAction$.subscribe(({ state, action }) => {this.next(state);scannedActions.next(action!);});
这样 state 流就产生了一个新 state, 从而通过 store.select 产生的 observable 的数据源就得到了更新,其 callback 就可以拿到正确的数据了
- reducer 流
state 中最关键的就是 reducer 到底是如何工作的。首先ReducerObservable就是ReducerManager,用的是 useExisting 声明的提供者
export const REDUCER_MANAGER_PROVIDERS: Provider[] = [ReducerManager,{ provide: ReducerObservable, useExisting: ReducerManager },{ provide: ReducerManagerDispatcher, useExisting: ActionsSubject },];
分析下 ReducerManager
constructor(private dispatcher: ReducerManagerDispatcher,@Inject(INITIAL_STATE) private initialState: any,@Inject(INITIAL_REDUCERS) private reducers: ActionReducerMap<any, any>,@Inject(REDUCER_FACTORY)private reducerFactory: ActionReducerFactory<any, any>) {super(reducerFactory(reducers, initialState));}
INITIAL_STATE,INITIAL_REDUCERS,REDUCER_FACTORY 就是正文1中B2,C3,D4,这要来看下 reducerFactory 的处理:
reducerFactory = createReducerFactory(combineReducers,reducersWithMetaReducers)
export function createReducerFactory<T, V extends Action = Action>(reducerFactory: ActionReducerFactory<T, V>,metaReducers?: MetaReducer<T, V>[]): ActionReducerFactory<T, V> {if (Array.isArray(metaReducers) && metaReducers.length > 0) {(reducerFactory2 as any) = compose.apply(null, [...metaReducers,reducerFactory,]);}return (reducers: ActionReducerMap<T, V>, initialState?: InitialState<T>) => {const reducer = reducerFactory2(reducers);return (state: T | undefined, action: V) => {state = state === undefined ? (initialState as T) : state;return reducer(state, action);};};}
export function compose(...functions: any[]) {return function (arg: any) {if (functions.length === 0) {return arg;}const last = functions[functions.length - 1];const rest = functions.slice(0, -1);return rest.reduceRight((composed, fn) => fn(composed), last(arg));};}
1、用 compose 产生一个新的 reducerFactory2,包含了 combineReducers 和 metaReducers,所以最终 reducerFactory 的值为:
return (reducers: ActionReducerMap<T, V>, initialState?: InitialState<T>) => {const reducer = reducerFactory2(reducers);return (state: T | undefined, action: V) => {state = state === undefined ? (initialState as T) : state;return reducer(state, action);};};
2、reducerFactory 在 reducer 流的构造函数内得到调用,产生了最终的 reducer
return (state: T | undefined, action: V) => {state = state === undefined ? (initialState as T) : state;return reducer(state, action);};
3、reducerFactory调用时执行了compose产生的reducerFactory2,从而 combineReducers 得到执行,并把 reducers 作为参数传进去
export function combineReducers(reducers: any,initialState: any = {}): ActionReducer<any, Action> {const reducerKeys = Object.keys(reducers);const finalReducers: any = {};for (let i = 0; i < reducerKeys.length; i++) {const key = reducerKeys[i];if (typeof reducers[key] === 'function') {finalReducers[key] = reducers[key];}}const finalReducerKeys = Object.keys(finalReducers);return function combination(state, action) {state = state === undefined ? initialState : state;let hasChanged = false;const nextState: any = {};for (let i = 0; i < finalReducerKeys.length; i++) {const key = finalReducerKeys[i];const reducer: any = finalReducers[key];const previousStateForKey = state[key];const nextStateForKey = reducer(previousStateForKey, action);nextState[key] = nextStateForKey;hasChanged = hasChanged || nextStateForKey !== previousStateForKey;}return hasChanged ? nextState : state;};}
a、combineReducers 对 reducers 做了过滤,只有 value 为 function 才会进入state
b、通过遍历 finalReducerKeys 的方式找到对应的 reducer 可能是一个性能问题
c、state 有没有变化的判断依据是引用判断,所以 reducer 往往返回一个新对象
4、最终生成的 reducer 几乎就是 combineReducers 返回的 combination 函数,只是将 metaReducer 也整合了进去,metaReducer 就是中间件,接受一个 reducer 函数,再返回一个 reducer 函数
5、compose 用的是 reduceRight,所以多个中间件情况下,将从左到右执行
到这里,@ngrx/store 主体流程基本就走完了,概括下:store发一个action,被 state 监听到,state 通过 reducer 产生一个新 state,被 store 监听到,通过select 产生的 Observable 们的 callback 得到执行,取到了正确的 state
正文3 - StoreModule.forFeature
其依赖注入部分就不讲了,和上面类似,主要看下它是如何进入根 store 的
constructor(@Inject(_STORE_FEATURES) private features: StoreFeature<any, any>[],@Inject(FEATURE_REDUCERS) private featureReducers: ActionReducerMap<any>[],private reducerManager: ReducerManager,root: StoreRootModule,@Optional()@Inject(_ACTION_TYPE_UNIQUENESS_CHECK)actionCheck: any) {const feats = features.map((feature, index) => {const featureReducerCollection = featureReducers.shift();const reducers = featureReducerCollection /*TODO(#823)*/![index];return {...feature,reducers,initialState: _initialStateFactory(feature.initialState),};});reducerManager.addFeatures(feats);}
可以看到依赖了 reducerManager,其底层调用了 reducerFactory,这里的this.reducers已经整合了 featureReducer 了,我们知道 this.reducerFactory 就是产生一个新的总 reducer,因此通过这种方式,forFeature 注册的子 state 就得以并入根 state 了
private updateReducers(featureKeys: string[]) {this.next(this.reducerFactory(this.reducers, this.initialState));this.dispatcher.next(<Action>{type: UPDATE,features: featureKeys,});}
- createFeatureSelector
主要看下其记忆功能是如何实现的
export function createFeatureSelector(featureName: any): MemoizedSelector<any, any> {return createSelector((state: any) => {const featureState = state[featureName];return featureState;},(featureState: any) => featureState);}export function createSelector(...input: any[]): MemoizedSelector<any, any> | MemoizedSelectorWithProps<any, any, any> {return createSelectorFactory(defaultMemoize)(...input);}export function createSelectorFactory(memoize: MemoizeFn,options: SelectorFactoryConfig<any, any> = {stateFn: defaultStateFn,}) {return function(...input: any[]): MemoizedSelector<any, any> | MemoizedSelectorWithProps<any, any, any> {let args = input;if (Array.isArray(args[0])) {const [head, ...tail] = args;args = [...head, ...tail];}const selectors = args.slice(0, args.length - 1);const projector = args[args.length - 1];const memoizedSelectors = selectors.filter((selector: any) =>selector.release && typeof selector.release === 'function');const memoizedProjector = memoize(function(...selectors: any[]) {return projector.apply(null, selectors);});const memoizedState = defaultMemoize(function(state: any, props: any) {return options.stateFn.apply(null, [state,selectors,props,memoizedProjector,]);});function release() {memoizedState.reset();memoizedProjector.reset();memoizedSelectors.forEach(selector => selector.release());}return Object.assign(memoizedState.memoized, {release,projector: memoizedProjector.memoized,setResult: memoizedState.setResult,clearResult: memoizedState.clearResult,});};}
简单来说,就是把上次运行的参数和结果给保存起来,下次运行时对比参数是否改变,未改变直接返回上次结果,否则重新执行得到新结果,并缓存此次参数和结果。其原理类似下面这个 memoize 函数:
function memoize(fn) {let lastArg;let lastResult;function cacheFn() {if (!lastArg) {lastArg = Array.from(arguments);lastResult = fn.apply(null, lastArg);return lastResult;}if (!isArgumentChanged(lastArg, Array.from(arguments))) {return lastResult;}lastArg = Array.from(arguments);lastResult = fn.apply(null, lastArg);return lastResult;}function reset() {lastArg = undefined;lastResult = undefined;}return Object.assign(cacheFn, {reset});}function isArgumentChanged(old, fresh) {return old.every((value, index) => value === fresh[index]);}// 使用function add(a,b){return a+b}const memoizeAdd = memoize(add)memoizeAdd(3,4) // 第一次计算并缓存memoizeAdd(3,4) // 直接返回memoizeAdd.reset() // 重置memoizeAdd(3,4) // 再次计算并缓存
ngrx 实现的较为复杂,搞了两层记忆:第一层只要 state 没变,下次直接返回结果;第二层如果 state 变了,但是其 selector 函数返回的值没变,仍然直接返回
后序
Ngrx 库使用TS写的,其中有些高级用法也简单学习下
- extends
export declare interface TypedAction<T extends string> extends Action {readonly type: T;}export type ActionCreator<T extends string = string,C extends Creator = Creator> = C & TypedAction<T>;// extends 更多用法,可以当个获取类型的函数了type TypeName<T> =T extends string ? "string" :T extends number ? "number" :T extends boolean ? "boolean" :T extends undefined ? "undefined" :T extends Function ? "function" :"object";type T0 = TypeName<string>; // "string"type T1 = TypeName<"a">; // "string"type T2 = TypeName<true>; // "boolean"type T3 = TypeName<() => void>; // "function"type T4 = TypeName<string[]>; // "object"
- 字符串当类型
export const arraysAreNotAllowedMsg ='arrays are not allowed in action creators';type ArraysAreNotAllowed = typeof arraysAreNotAllowedMsg;
- unknown
unknown 字面理解和 any 其实没差,任何类型都可赋值给它,但有一点,unknown 类型不能赋值给除了 unknown 或 any 的其他任何类型,使用前必需显式进行指定类型,或是在有条件判断情况下能够隐式地进行类型推断的情况。
