NgRx:多次调用CustomRouterStateSerializer serialize()

时间:2019-08-15 10:15:58

标签: angular ngrx ngrx-store ngrx-router-store

我已经以通用方式(https://ngrx.io/guide/router-store/configuration)为Angular项目实现了NgRx路由器存储。

我的“问题”是,当我通过单击组件的相应html元素触发routerLink时,我的CustomRouterStateSerializer的序列化方法似乎被多次调用。

您将在StackBlitz上找到以下说明的最小示例应用程序。


我的实现

这是我的router.reducer.ts文件,其中包含RouterStateUrl接口和Serializer-Class:

export interface RouterStateUrl  {
    url: string;
    queryParams: Params;
    params: Params;
    random: number;
}

export class CustomRouterStateSerializer implements RouterStateSerializer<RouterStateUrl > {
    serialize(routerState: RouterStateSnapshot): RouterStateUrl  {
        const { url, root: { queryParams } } = routerState;

        // Random number to be able to match console output to router-state later (with NgRx Store DevTools)
        const random = Math.random();
        console.warn(`CustomRouterStateSerializer called by ${url}, random: ${random}`);

        let state: ActivatedRouteSnapshot = routerState.root;
        while(state.firstChild){
            state = state.firstChild;
        }
        const { params } = state;
        return {url, queryParams, params, random };
    }
}

这是我的app.module.ts文件:

/*[...]*/
imports: [
    /*[...],*/
    StoreModule.forRoot(reducers, {
        metaReducers,
        runtimeChecks: {
            strictStateImmutability: true,
            strictActionImmutability: true
        }
    }),
    StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.development }),
    EffectsModule.forRoot([AppEffects]),
    StoreRouterConnectingModule.forRoot({
        serializer: CustomRouterStateSerializer,
        navigationActionTiming: NavigationActionTiming.PostActivation,
    }),
]
/*[...]*/


详细说明和输出

可以说我的应用当前显示了一些项目的概述(URL:/ projects),并且routerLink被触发以切换组件以显示作业的概述(URL:/ jobs)。控制台将打印三则消息:

  • CustomRouterStateSerializer called by /projects, random: 0.0896547559010431

  • CustomRouterStateSerializer called by /jobs, random: 0.7662025752972623

  • CustomRouterStateSerializer called by /jobs, random: 0.07919176016307328

NgRx Store DevTools显示了预期的几个操作:

@ngrx/router-store/request

router: {
    state: {
        url: '/projects',
        queryParams: {},
        params: {},
        random: 0.31957045879116797
    },
    navigationId: 2
}

@ngrx/router-store/navigation

router: {
    state: {
        url: '/jobs',
        queryParams: {},
        params: {},
        random: 0.7662025752972623
    },
    navigationId: 3
}

@ngrx/router-store/navigated

router: {
    state: {
      url: '/jobs',
      queryParams: {},
      params: {},
      random: 0.7662025752972623
    },
    navigationId: 3
}

您可以看到@ngrx/router-store/navigation@ngrx/router-store/navigated的状态是相同的。此外,它们的随机数与第二个控制台输出相同。 @ngrx/router-store/request的随机数属于旧项目视图的状态。

NgRx Store DevTools的输出似乎与预期的一样。但是我不明白何时何地触发了其他控制台输出的序列化方法。我在任何状态下都找不到第一和第三控制台输出的任何随机数。现在,我要问自己是不是犯了一个错误(正在实现某些东西),或者这仅仅是正常现象(但是为什么?)。也许有些人可以告诉我。

1 个答案:

答案 0 :(得分:1)

我尝试了一些方法,但最终它有助于查看@ngrx/router-store的源代码。 该模块的本质作用是监听所有路由器事件和调度动作。重要的代码是以下代码:https://github.com/ngrx/platform/blob/master/modules/router-store/src/router_store_module.ts#L240-L275

  

但是我不明白何时以及什么触发了其他控制台输出的序列化方法。

在这里您可以看到在三种情况下可以调用serialize()

  • 直接在NavigationStart
  • NavigationEnd(通过dispatchRouterNavigation()dispatchRouterNavigated()方法)
  • RoutesRecognized(通过dispatchRouterNavigated()

每个事件都分别调用serialize()以避免派生状态:路由器是路由器状态的源,它可以在任何给定时间更改。 因此,当需要序列化状态时,它将不会存储在某个位置,而是每次都会重新计算。 这就是为什么该函数被多次调用的原因。但是,由于串行器功能应该是纯函数,所以这根本不是问题,而只是设计的一部分。

  

在任何状态下都找不到第一和第三控制台输出的任何随机数。

reducer只会将路由器状态放入navigationcancelerror上的存储中,而不会放入request和{{1} }。 这意味着,您在商店中看到的唯一随机数是源自navigated动作的随机数。 其他所有东西都只是在“途中”使用。

  

现在,我要问自己是不是犯了一个错误(正在实施某些东西),或者这仅仅是正常行为(但是为什么?)。

您的实现看起来不错,您可以放心:这是正常的和预期的行为! ?请注意,您的序列化函数应该是纯函数。

希望这会有所帮助!