NgRx:多次调用 CustomRouterStateSerializer serialize()

NgRx: CustomRouterStateSerializer serialize() called multiple times

我已经以常用方式 (https://ngrx.io/guide/router-store/configuration) 为我的 Angular 项目实现了一个 NgRx Router-Store。

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

您将在 StackBlitz.

上找到以下描述的最小示例应用程序

我的实现

这是我的 router.reducer.ts 文件,其中包含 RouterStateUrl-Interface 和 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 ).控制台将打印三个消息:

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的随机数属于旧projects-view的状态

NgRx Store DevTools 的输出似乎符合预期。但我不明白何时以及什么调用了其他控制台输出被触发的序列化方法。我在任何状态下都找不到第一个和第三个控制台输出的任何随机数。现在我问自己是不是我犯了错误(实现东西)或者这是否只是正常行为(但为什么?)。也许你们中的一些人可以告诉我。

我尝试了一些东西,但最终还是有助于查看 @ngrx/router-store 的源代码。 这个模块本质上做的是监听所有的路由器事件和派发动作。重要的一段代码是:https://github.com/ngrx/platform/blob/master/modules/router-store/src/router_store_module.ts#L240-L275

But I don't understand when and what called the serialize method where the other console outputs were triggert.

这里可以看到可以调用serialize()的三种情况:

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

这些事件中的每一个都单独调用 serialize() 以避免派生状态:路由器是路由器状态的来源,它可以随时更改。 因此,当需要序列化状态时,它不会存储在某个地方,而是每次都重新计算。 这就是多次调用该函数的原因。但是,由于序列化器函数应该是纯函数,所以这根本不是问题,而是设计的一部分。

I can not find any random numbers of the first and the third console output in any state.

reducer 只会将路由器状态放入 navigationcancelerror 的存储中 – 但不会在 requestnavigated。 这意味着,您在商店中看到的唯一随机数是来自 navigation 操作的随机数。 所有其他的都只是使用 "on the way".

Now I'm asking myself if I have made an error (implementing stuff) or if this is just normal behaviour (but why?).

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

希望对您有所帮助!