前言

@ngrx/store 作为 Angular 的状态管理库,在实现上必然离不开依赖注入,所以不清楚依赖注入概念的请先读这篇文章了解下。

正文1 - 依赖注入

StoreModule.forRoot()

  1. static forRoot(
  2. reducers:
  3. | ActionReducerMap<any, any>
  4. | InjectionToken<ActionReducerMap<any, any>>,
  5. config: RootStoreConfig<any, any> = {}
  6. ): ModuleWithProviders<StoreRootModule> {
  7. return {
  8. ngModule: StoreRootModule,
  9. providers: [
  10. // A
  11. {
  12. provide: _ROOT_STORE_GUARD,
  13. useFactory: _provideForRootGuard,
  14. deps: [[Store, new Optional(), new SkipSelf()]],
  15. },
  16. // B1
  17. { provide: _INITIAL_STATE, useValue: config.initialState },
  18. // B2
  19. {
  20. provide: INITIAL_STATE,
  21. useFactory: _initialStateFactory,
  22. deps: [_INITIAL_STATE],
  23. },
  24. // C1
  25. { provide: _INITIAL_REDUCERS, useValue: reducers },
  26. // C2
  27. {
  28. provide: _STORE_REDUCERS,
  29. useExisting:
  30. reducers instanceof InjectionToken ? reducers : _INITIAL_REDUCERS,
  31. },
  32. // C3
  33. {
  34. provide: INITIAL_REDUCERS,
  35. deps: [Injector, _INITIAL_REDUCERS, [new Inject(_STORE_REDUCERS)]],
  36. useFactory: _createStoreReducers,
  37. },
  38. // D1
  39. {
  40. provide: USER_PROVIDED_META_REDUCERS,
  41. useValue: config.metaReducers ? config.metaReducers : [],
  42. },
  43. // D2
  44. {
  45. provide: _RESOLVED_META_REDUCERS,
  46. deps: [META_REDUCERS, USER_PROVIDED_META_REDUCERS],
  47. useFactory: _concatMetaReducers,
  48. },
  49. // D3
  50. {
  51. provide: _REDUCER_FACTORY,
  52. useValue: config.reducerFactory
  53. ? config.reducerFactory
  54. : combineReducers,
  55. },
  56. // D4
  57. {
  58. provide: REDUCER_FACTORY,
  59. deps: [_REDUCER_FACTORY, _RESOLVED_META_REDUCERS],
  60. useFactory: createReducerFactory,
  61. },
  62. // J
  63. ACTIONS_SUBJECT_PROVIDERS,
  64. // K
  65. REDUCER_MANAGER_PROVIDERS,
  66. // L
  67. SCANNED_ACTIONS_SUBJECT_PROVIDERS,
  68. // M
  69. STATE_PROVIDERS,
  70. // N
  71. STORE_PROVIDERS,
  72. // O
  73. provideRuntimeChecks(config.runtimeChecks),
  74. // P
  75. checkForActionTypeUniqueness(),
  76. ],
  77. };
  78. }

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)

  1. export function _initialStateFactory(initialState: any): any {
  2. if (typeof initialState === 'function') {
  3. return initialState();
  4. }
  5. return initialState;
  6. }

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个依赖,其实现却只用了前两个依赖,实际上第二个依赖和第三个依赖是回事

  1. {
  2. provide: INITIAL_REDUCERS,
  3. deps: [Injector, _INITIAL_REDUCERS, [new Inject(_STORE_REDUCERS)]],
  4. useFactory: _createStoreReducers,
  5. }
  6. export function _createStoreReducers(
  7. injector: Injector,
  8. reducers: ActionReducerMap<any, any>
  9. ) {
  10. return reducers instanceof InjectionToken ? injector.get(reducers) : reducers;
  11. }

搞了这么多,实际上都是参数处理过程,使用的时候用C3就完事了

  • D1,D2,D3,D4

和前面类似,都是处理参数,这次是 config.metaReducers 和 config.reducerFactory

D1 = config.metaReducers || []
D2 = _concatMetaReducers(META_REDUCERS,D1)

  1. export function _concatMetaReducers(
  2. metaReducers: MetaReducer[],
  3. userProvidedMetaReducers: MetaReducer[]
  4. ): MetaReducer[] {
  5. return metaReducers.concat(userProvidedMetaReducers);
  6. }

D3 = config.reducerFactory??combineReducers
D4 = createReducerFactory(D3,D2)

  • J
    一个 BehaviorSubject 的子类,用于传递 action,称为 action 流
  1. export const INIT = '@ngrx/store/init' as '@ngrx/store/init';
  2. @Injectable()
  3. export class ActionsSubject extends BehaviorSubject<Action>
  4. implements OnDestroy {
  5. constructor() {
  6. super({ type: INIT });
  7. }
  8. next(action: Action): void {
  9. super.next(action);
  10. }
  11. complete() {
  12. /* noop */
  13. }
  14. ngOnDestroy() {
  15. super.complete();
  16. }
  17. }
  • K
    这里就注入了 B2,C3,D4,是一个核心函数,用于 reducer 管理
  1. @Injectable()
  2. export class ReducerManager extends BehaviorSubject<ActionReducer<any, any>>
  3. implements OnDestroy {
  4. constructor(
  5. private dispatcher: ReducerManagerDispatcher,
  6. @Inject(INITIAL_STATE) private initialState: any,
  7. @Inject(INITIAL_REDUCERS) private reducers: ActionReducerMap<any, any>,
  8. @Inject(REDUCER_FACTORY)
  9. private reducerFactory: ActionReducerFactory<any, any>
  10. ) {
  11. super(reducerFactory(reducers, initialState));
  12. }
  13. addFeature(feature: StoreFeature<any, any>) {
  14. this.addFeatures([feature]);
  15. }
  16. addFeatures(features: StoreFeature<any, any>[]) {
  17. const reducers = features.reduce(
  18. (
  19. reducerDict,
  20. { reducers, reducerFactory, metaReducers, initialState, key }
  21. ) => {
  22. const reducer =
  23. typeof reducers === 'function'
  24. ? createFeatureReducerFactory(metaReducers)(reducers, initialState)
  25. : createReducerFactory(reducerFactory, metaReducers)(
  26. reducers,
  27. initialState
  28. );
  29. reducerDict[key] = reducer;
  30. return reducerDict;
  31. },
  32. {} as { [key: string]: ActionReducer<any, any> }
  33. );
  34. this.addReducers(reducers);
  35. }
  36. removeFeature(feature: StoreFeature<any, any>) {
  37. this.removeFeatures([feature]);
  38. }
  39. removeFeatures(features: StoreFeature<any, any>[]) {
  40. this.removeReducers(features.map((p) => p.key));
  41. }
  42. addReducer(key: string, reducer: ActionReducer<any, any>) {
  43. this.addReducers({ [key]: reducer });
  44. }
  45. addReducers(reducers: { [key: string]: ActionReducer<any, any> }) {
  46. this.reducers = { ...this.reducers, ...reducers };
  47. this.updateReducers(Object.keys(reducers));
  48. }
  49. removeReducer(featureKey: string) {
  50. this.removeReducers([featureKey]);
  51. }
  52. removeReducers(featureKeys: string[]) {
  53. featureKeys.forEach((key) => {
  54. this.reducers = omit(this.reducers, key) /*TODO(#823)*/ as any;
  55. });
  56. this.updateReducers(featureKeys);
  57. }
  58. private updateReducers(featureKeys: string[]) {
  59. this.next(this.reducerFactory(this.reducers, this.initialState));
  60. this.dispatcher.next(<Action>{
  61. type: UPDATE,
  62. features: featureKeys,
  63. });
  64. }
  65. ngOnDestroy() {
  66. this.complete();
  67. }
  68. }
  • L
    ScannedActionsSubject 目前啥都没做,只是一个 Subject 的子类,后续主要用于 ngrx/store 功能的扩展,@store/effect 就用到了这个
  1. @Injectable()
  2. export class ScannedActionsSubject extends Subject<Action>
  3. implements OnDestroy {
  4. ngOnDestroy() {
  5. this.complete();
  6. }
  7. }
  • M
    state 流
  1. @Injectable()
  2. export class State<T> extends BehaviorSubject<any> implements OnDestroy {
  3. static readonly INIT = INIT;
  4. private stateSubscription: Subscription;
  5. constructor(
  6. actions$: ActionsSubject,
  7. reducer$: ReducerObservable,
  8. scannedActions: ScannedActionsSubject,
  9. @Inject(INITIAL_STATE) initialState: any
  10. ) {
  11. super(initialState);
  12. const actionsOnQueue$: Observable<Action> = actions$.pipe(
  13. observeOn(queueScheduler)
  14. );
  15. const withLatestReducer$: Observable<[
  16. Action,
  17. ActionReducer<any, Action>
  18. ]> = actionsOnQueue$.pipe(withLatestFrom(reducer$));
  19. const seed: StateActionPair<T> = { state: initialState };
  20. const stateAndAction$: Observable<{
  21. state: any;
  22. action?: Action;
  23. }> = withLatestReducer$.pipe(
  24. scan<[Action, ActionReducer<T, Action>], StateActionPair<T>>(
  25. reduceState,
  26. seed
  27. )
  28. );
  29. this.stateSubscription = stateAndAction$.subscribe(({ state, action }) => {
  30. this.next(state);
  31. scannedActions.next(action!);
  32. });
  33. }
  34. ngOnDestroy() {
  35. this.stateSubscription.unsubscribe();
  36. this.complete();
  37. }
  38. }
  • N

store

  1. @Injectable()
  2. export class Store<T = object> extends Observable<T>
  3. implements Observer<Action> {
  4. constructor(
  5. state$: StateObservable,
  6. private actionsObserver: ActionsSubject,
  7. private reducerManager: ReducerManager
  8. ) {
  9. super();
  10. this.source = state$;
  11. }
  12. select<Props = any, K = any>(
  13. pathOrMapFn: ((state: T, props?: Props) => K) | string,
  14. ...paths: string[]
  15. ): Observable<any> {
  16. return (select as any).call(null, pathOrMapFn, ...paths)(this);
  17. }
  18. lift<R>(operator: Operator<T, R>): Store<R> {
  19. const store = new Store<R>(this, this.actionsObserver, this.reducerManager);
  20. store.operator = operator;
  21. return store;
  22. }
  23. dispatch<V extends Action = Action>(
  24. action: V &
  25. FunctionIsNotAllowed<
  26. V,
  27. 'Functions are not allowed to be dispatched. Did you forget to call the action creator function?'
  28. >
  29. ) {
  30. this.actionsObserver.next(action);
  31. }
  32. next(action: Action) {
  33. this.actionsObserver.next(action);
  34. }
  35. error(err: any) {
  36. this.actionsObserver.error(err);
  37. }
  38. complete() {
  39. this.actionsObserver.complete();
  40. }
  41. addReducer<State, Actions extends Action = Action>(
  42. key: string,
  43. reducer: ActionReducer<State, Actions>
  44. ) {
  45. this.reducerManager.addReducer(key, reducer);
  46. }
  47. removeReducer<Key extends Extract<keyof T, string>>(key: Key) {
  48. this.reducerManager.removeReducer(key);
  49. }
  50. }

到这里为止,ngrx/store 里的一些关键函数都得到了注册。

正文2 - StoreRootModule

下面看调用过程:forRoot 方法除了返回一堆 provider 外,还返回了 StoreRootModule,StoreRootModule会被 Angular 初始化,从而其constructor中那些依赖会被注入,也即上面的 provider 会被初始化。

  1. @NgModule({})
  2. export class StoreRootModule {
  3. constructor(
  4. actions$: ActionsSubject,
  5. reducer$: ReducerObservable,
  6. scannedActions$: ScannedActionsSubject,
  7. store: Store<any>,
  8. @Optional()
  9. @Inject(_ROOT_STORE_GUARD)
  10. guard: any,
  11. @Optional()
  12. @Inject(_ACTION_TYPE_UNIQUENESS_CHECK)
  13. actionCheck: any
  14. ) {}
  15. }
  • Store
    Store 有三个依赖:state流,action流和 reducerManager,有 dispatch 和 select 两个常用方法,dispatch 实际上就是调用 action流的next 方法让 action 向下流动,这里主要看下 select 方法
  1. select<Props = any, K = any>(
  2. pathOrMapFn: ((state: T, props?: Props) => K) | string,
  3. ...paths: string[]
  4. ): Observable<any> {
  5. return (select as any).call(null, pathOrMapFn, ...paths)(this);
  6. }
  7. export function select<T, Props, K>(
  8. pathOrMapFn: ((state: T, props?: Props) => any) | string,
  9. propsOrPath?: Props | string,
  10. ...paths: string[]
  11. ) {
  12. return function selectOperator(source$: Observable<T>): Observable<K> {
  13. let mapped$: Observable<any>;
  14. if (typeof pathOrMapFn === 'string') {
  15. const pathSlices = [<string>propsOrPath, ...paths].filter(Boolean);
  16. mapped$ = source$.pipe(pluck(pathOrMapFn, ...pathSlices));
  17. } else if (typeof pathOrMapFn === 'function') {
  18. mapped$ = source$.pipe(
  19. map(source => pathOrMapFn(source, <Props>propsOrPath))
  20. );
  21. } else {
  22. throw new TypeError(
  23. `Unexpected type '${typeof pathOrMapFn}' in select operator,` +
  24. ` expected 'string' or 'function'`
  25. );
  26. }
  27. return mapped$.pipe(distinctUntilChanged());
  28. };
  29. }

注意 select 函数返回的最终值是 source$.pipe(…).pipe(distinctUntilChanged()),而source$ 就是依赖的 state 流。所以只要 state 流有新值产生,source$.pipe(…).pipe(distinctUntilChanged())的观察者们就会执行一遍,这也是为什么在不同的地方调用 this.store.select(‘xxx’).subcribe(callback) 后,不论在哪里执行 dispatch,callback 都有机会执行(distinctUntilChanged的存在导致callback不一定执行)

  • state 流
  1. constructor(
  2. actions$: ActionsSubject,
  3. reducer$: ReducerObservable,
  4. scannedActions: ScannedActionsSubject,
  5. @Inject(INITIAL_STATE) initialState: any
  6. ) {
  7. super(initialState);
  8. const actionsOnQueue$: Observable<Action> = actions$.pipe(
  9. observeOn(queueScheduler)
  10. );
  11. const withLatestReducer$: Observable<[
  12. Action,
  13. ActionReducer<any, Action>
  14. ]> = actionsOnQueue$.pipe(withLatestFrom(reducer$));
  15. const seed: StateActionPair<T> = { state: initialState };
  16. const stateAndAction$: Observable<{
  17. state: any;
  18. action?: Action;
  19. }> = withLatestReducer$.pipe(
  20. scan<[Action, ActionReducer<T, Action>], StateActionPair<T>>(
  21. reduceState,
  22. seed
  23. )
  24. );
  25. this.stateSubscription = stateAndAction$.subscribe(({ state, action }) => {
  26. this.next(state);
  27. scannedActions.next(action!);
  28. });
  29. }

1、其本身是一个BehaviorSubject,产出 state
2、订阅了 action 流的变化
3、使用 scan + reduceState来迭代 state 状态

  1. export function reduceState<T, V extends Action = Action>(
  2. stateActionPair: StateActionPair<T, V> = { state: undefined },
  3. [action, reducer]: [V, ActionReducer<T, V>]
  4. ): StateActionPair<T, V> {
  5. const { state } = stateActionPair;
  6. return { state: reducer(state, action), action };
  7. }

从这儿可以看出,每当有action触发,都会进入 reduceState, 产生一个新 state,组成 {state,action}对传给订阅者

  1. this.stateSubscription = stateAndAction$.subscribe(({ state, action }) => {
  2. this.next(state);
  3. scannedActions.next(action!);
  4. });

这样 state 流就产生了一个新 state, 从而通过 store.select 产生的 observable 的数据源就得到了更新,其 callback 就可以拿到正确的数据了

  • reducer 流
    state 中最关键的就是 reducer 到底是如何工作的。首先ReducerObservable就是ReducerManager,用的是 useExisting 声明的提供者
  1. export const REDUCER_MANAGER_PROVIDERS: Provider[] = [
  2. ReducerManager,
  3. { provide: ReducerObservable, useExisting: ReducerManager },
  4. { provide: ReducerManagerDispatcher, useExisting: ActionsSubject },
  5. ];

分析下 ReducerManager

  1. constructor(
  2. private dispatcher: ReducerManagerDispatcher,
  3. @Inject(INITIAL_STATE) private initialState: any,
  4. @Inject(INITIAL_REDUCERS) private reducers: ActionReducerMap<any, any>,
  5. @Inject(REDUCER_FACTORY)
  6. private reducerFactory: ActionReducerFactory<any, any>
  7. ) {
  8. super(reducerFactory(reducers, initialState));
  9. }

INITIAL_STATE,INITIAL_REDUCERS,REDUCER_FACTORY 就是正文1中B2,C3,D4,这要来看下 reducerFactory 的处理:

  1. reducerFactory = createReducerFactory(combineReducers,reducersWithMetaReducers)
  1. export function createReducerFactory<T, V extends Action = Action>(
  2. reducerFactory: ActionReducerFactory<T, V>,
  3. metaReducers?: MetaReducer<T, V>[]
  4. ): ActionReducerFactory<T, V> {
  5. if (Array.isArray(metaReducers) && metaReducers.length > 0) {
  6. (reducerFactory2 as any) = compose.apply(null, [
  7. ...metaReducers,
  8. reducerFactory,
  9. ]);
  10. }
  11. return (reducers: ActionReducerMap<T, V>, initialState?: InitialState<T>) => {
  12. const reducer = reducerFactory2(reducers);
  13. return (state: T | undefined, action: V) => {
  14. state = state === undefined ? (initialState as T) : state;
  15. return reducer(state, action);
  16. };
  17. };
  18. }
  1. export function compose(...functions: any[]) {
  2. return function (arg: any) {
  3. if (functions.length === 0) {
  4. return arg;
  5. }
  6. const last = functions[functions.length - 1];
  7. const rest = functions.slice(0, -1);
  8. return rest.reduceRight((composed, fn) => fn(composed), last(arg));
  9. };
  10. }

1、用 compose 产生一个新的 reducerFactory2,包含了 combineReducers 和 metaReducers,所以最终 reducerFactory 的值为:

  1. return (reducers: ActionReducerMap<T, V>, initialState?: InitialState<T>) => {
  2. const reducer = reducerFactory2(reducers);
  3. return (state: T | undefined, action: V) => {
  4. state = state === undefined ? (initialState as T) : state;
  5. return reducer(state, action);
  6. };
  7. };

2、reducerFactory 在 reducer 流的构造函数内得到调用,产生了最终的 reducer

  1. return (state: T | undefined, action: V) => {
  2. state = state === undefined ? (initialState as T) : state;
  3. return reducer(state, action);
  4. };

3、reducerFactory调用时执行了compose产生的reducerFactory2,从而 combineReducers 得到执行,并把 reducers 作为参数传进去

  1. export function combineReducers(
  2. reducers: any,
  3. initialState: any = {}
  4. ): ActionReducer<any, Action> {
  5. const reducerKeys = Object.keys(reducers);
  6. const finalReducers: any = {};
  7. for (let i = 0; i < reducerKeys.length; i++) {
  8. const key = reducerKeys[i];
  9. if (typeof reducers[key] === 'function') {
  10. finalReducers[key] = reducers[key];
  11. }
  12. }
  13. const finalReducerKeys = Object.keys(finalReducers);
  14. return function combination(state, action) {
  15. state = state === undefined ? initialState : state;
  16. let hasChanged = false;
  17. const nextState: any = {};
  18. for (let i = 0; i < finalReducerKeys.length; i++) {
  19. const key = finalReducerKeys[i];
  20. const reducer: any = finalReducers[key];
  21. const previousStateForKey = state[key];
  22. const nextStateForKey = reducer(previousStateForKey, action);
  23. nextState[key] = nextStateForKey;
  24. hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
  25. }
  26. return hasChanged ? nextState : state;
  27. };
  28. }

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 的

  1. constructor(
  2. @Inject(_STORE_FEATURES) private features: StoreFeature<any, any>[],
  3. @Inject(FEATURE_REDUCERS) private featureReducers: ActionReducerMap<any>[],
  4. private reducerManager: ReducerManager,
  5. root: StoreRootModule,
  6. @Optional()
  7. @Inject(_ACTION_TYPE_UNIQUENESS_CHECK)
  8. actionCheck: any
  9. ) {
  10. const feats = features.map((feature, index) => {
  11. const featureReducerCollection = featureReducers.shift();
  12. const reducers = featureReducerCollection /*TODO(#823)*/![index];
  13. return {
  14. ...feature,
  15. reducers,
  16. initialState: _initialStateFactory(feature.initialState),
  17. };
  18. });
  19. reducerManager.addFeatures(feats);
  20. }

可以看到依赖了 reducerManager,其底层调用了 reducerFactory,这里的this.reducers已经整合了 featureReducer 了,我们知道 this.reducerFactory 就是产生一个新的总 reducer,因此通过这种方式,forFeature 注册的子 state 就得以并入根 state 了

  1. private updateReducers(featureKeys: string[]) {
  2. this.next(this.reducerFactory(this.reducers, this.initialState));
  3. this.dispatcher.next(<Action>{
  4. type: UPDATE,
  5. features: featureKeys,
  6. });
  7. }
  • createFeatureSelector
    主要看下其记忆功能是如何实现的
  1. export function createFeatureSelector(
  2. featureName: any
  3. ): MemoizedSelector<any, any> {
  4. return createSelector(
  5. (state: any) => {
  6. const featureState = state[featureName];
  7. return featureState;
  8. },
  9. (featureState: any) => featureState
  10. );
  11. }
  12. export function createSelector(
  13. ...input: any[]
  14. ): MemoizedSelector<any, any> | MemoizedSelectorWithProps<any, any, any> {
  15. return createSelectorFactory(defaultMemoize)(...input);
  16. }
  17. export function createSelectorFactory(
  18. memoize: MemoizeFn,
  19. options: SelectorFactoryConfig<any, any> = {
  20. stateFn: defaultStateFn,
  21. }
  22. ) {
  23. return function(
  24. ...input: any[]
  25. ): MemoizedSelector<any, any> | MemoizedSelectorWithProps<any, any, any> {
  26. let args = input;
  27. if (Array.isArray(args[0])) {
  28. const [head, ...tail] = args;
  29. args = [...head, ...tail];
  30. }
  31. const selectors = args.slice(0, args.length - 1);
  32. const projector = args[args.length - 1];
  33. const memoizedSelectors = selectors.filter(
  34. (selector: any) =>
  35. selector.release && typeof selector.release === 'function'
  36. );
  37. const memoizedProjector = memoize(function(...selectors: any[]) {
  38. return projector.apply(null, selectors);
  39. });
  40. const memoizedState = defaultMemoize(function(state: any, props: any) {
  41. return options.stateFn.apply(null, [
  42. state,
  43. selectors,
  44. props,
  45. memoizedProjector,
  46. ]);
  47. });
  48. function release() {
  49. memoizedState.reset();
  50. memoizedProjector.reset();
  51. memoizedSelectors.forEach(selector => selector.release());
  52. }
  53. return Object.assign(memoizedState.memoized, {
  54. release,
  55. projector: memoizedProjector.memoized,
  56. setResult: memoizedState.setResult,
  57. clearResult: memoizedState.clearResult,
  58. });
  59. };
  60. }

简单来说,就是把上次运行的参数和结果给保存起来,下次运行时对比参数是否改变,未改变直接返回上次结果,否则重新执行得到新结果,并缓存此次参数和结果。其原理类似下面这个 memoize 函数:

  1. function memoize(fn) {
  2. let lastArg;
  3. let lastResult;
  4. function cacheFn() {
  5. if (!lastArg) {
  6. lastArg = Array.from(arguments);
  7. lastResult = fn.apply(null, lastArg);
  8. return lastResult;
  9. }
  10. if (!isArgumentChanged(lastArg, Array.from(arguments))) {
  11. return lastResult;
  12. }
  13. lastArg = Array.from(arguments);
  14. lastResult = fn.apply(null, lastArg);
  15. return lastResult;
  16. }
  17. function reset() {
  18. lastArg = undefined;
  19. lastResult = undefined;
  20. }
  21. return Object.assign(cacheFn, {
  22. reset
  23. });
  24. }
  25. function isArgumentChanged(old, fresh) {
  26. return old.every((value, index) => value === fresh[index]);
  27. }
  28. // 使用
  29. function add(a,b){
  30. return a+b
  31. }
  32. const memoizeAdd = memoize(add)
  33. memoizeAdd(3,4) // 第一次计算并缓存
  34. memoizeAdd(3,4) // 直接返回
  35. memoizeAdd.reset() // 重置
  36. memoizeAdd(3,4) // 再次计算并缓存

ngrx 实现的较为复杂,搞了两层记忆:第一层只要 state 没变,下次直接返回结果;第二层如果 state 变了,但是其 selector 函数返回的值没变,仍然直接返回

后序

Ngrx 库使用TS写的,其中有些高级用法也简单学习下

  • extends
  1. export declare interface TypedAction<T extends string> extends Action {
  2. readonly type: T;
  3. }
  4. export type ActionCreator<
  5. T extends string = string,
  6. C extends Creator = Creator
  7. > = C & TypedAction<T>;
  8. // extends 更多用法,可以当个获取类型的函数了
  9. type TypeName<T> =
  10. T extends string ? "string" :
  11. T extends number ? "number" :
  12. T extends boolean ? "boolean" :
  13. T extends undefined ? "undefined" :
  14. T extends Function ? "function" :
  15. "object";
  16. type T0 = TypeName<string>; // "string"
  17. type T1 = TypeName<"a">; // "string"
  18. type T2 = TypeName<true>; // "boolean"
  19. type T3 = TypeName<() => void>; // "function"
  20. type T4 = TypeName<string[]>; // "object"
  • 字符串当类型
  1. export const arraysAreNotAllowedMsg =
  2. 'arrays are not allowed in action creators';
  3. type ArraysAreNotAllowed = typeof arraysAreNotAllowedMsg;
  • unknown
    unknown 字面理解和 any 其实没差,任何类型都可赋值给它,但有一点,unknown 类型不能赋值给除了 unknown 或 any 的其他任何类型,使用前必需显式进行指定类型,或是在有条件判断情况下能够隐式地进行类型推断的情况。