[UPDATE: Ok。]我们发现了如何复制,参见最小复制]
描述我们在生产环境和非生产环境的服务器日志中看到许多这两个致命错误:
- FATAL [main.js:xx] ngxsselectsnapshotmodulenotimported [Error]:您忘记导入"NgxsSelectSnapshotModule"!/opt/app/dist/server/main.js:xx:yy) at getStore
- FATAL [main.js:xx]错误:您忘记导入NGXS模块!at createSelectObservable (/opt/app/dist/server/main.js:1:yy)
我们显然已经在AppModule和Feature modules中导入了两个ngxs模块。App模块:
NgxsModule.forRoot(
[
xxx....
],
{
developmentMode: !environment.production,
},
),
NgxsRouterPluginModule.forRoot(),
NgxsFormPluginModule.forRoot(),
NgxsSelectSnapshotModule.forRoot(),
NgxsStoragePluginModule.forRoot({
...xxx
},
}),
NgxsReduxDevtoolsPluginModule.forRoot({
name: 'xxxx',
disabled: environment.production,
}),
in Feature Module:
NgxsModule.forFeature([xxx, yyy]),
NgxsSelectSnapshotModule,
这是一个非常庞大的项目,有许多模块和组件…错误只会通过SSR发生,在我们的开发机器上很少发生……但是,当它这样做时,我们会看到大量的Error语句相继出现,而没有其他组件或模块报错。它只是ngxs。看起来,SSR仍然在交付html, Angular应用在客户端上的加载仍然很好(包括所有ngxs的东西)
🔬最小复制
Ok。我们发现了如何复制:
- 在浏览器中打开一个页面
- 快速中止加载
- 再次快速刷新
- ——比;许多许多致命错误
猜测:这可能是由于angular销毁了所有的东西,两个ngxs模块都会清除注入器的onDestroy。因为只有当我们在中止后快速重新加载时才会出现错误(仅仅中止永远不会产生错误),所以在ngxs已经摆脱了它的注入器引用的情况下,使用一些组件时,它一定是angular通用的。
https://github.com/ngxs-labs/select-snapshot/blob/0b3b7fea09cd6db9fd7327f2b2710d24391e75a1/src/lib/core/internals/static-injector.ts L27
🔥异常或错误
FATAL [main.js:2897] Error: You have forgotten to import the NGXS module!
at createSelectObservable (/opt/app/dist/server/main.js:1:xx)
at AppComponent.get [as progress] (/opt/app/dist/server/main.js:1:xx)
at AppComponent_Template (/opt/app/dist/server/main.js:2897:1625353)
at executeTemplate (/opt/app/dist/server/main.js:2897:xx)
at refreshView (/opt/app/dist/server/main.js:2897:xx)
at refreshComponent (/opt/app/dist/server/main.js:2897:xx)
at refreshChildComponents (/opt/app/dist/server/main.js:2897:xx)
at refreshView (/opt/app/dist/server/main.js:2897:xx)
at renderComponentOrTemplate (/opt/app/dist/server/main.js:2897:xx)
at tickRootContext (/opt/app/dist/server/main.js:2897:xx)
or....
FATAL [main.js:2897] NgxsSelectSnapshotModuleIsNotImported [Error]: You've forgotten to import "NgxsSelectSnapshotModule"!
at assertDefined (/opt/app/dist/server/main.js:2897:xx)
at getStore (/opt/app/dist/server/main.js:2897:xx)
at XXXComponent.get [as isXXX] (/opt/app/dist/server/main.js:2897:xx)
at XXXComponent_Template (/opt/app/dist/server/main.js:2897:xx)
at executeTemplate (/opt/app/dist/server/main.js:2897:xx)
at refreshView (/opt/app/dist/server/main.js:2897:xx)
at refreshComponent (/opt/app/dist/server/main.js:2897:xx)
at refreshChildComponents (/opt/app/dist/server/main.js:2897:xx)
at refreshView (/opt/app/dist/server/main.js:2897:xx)
at refreshComponent (/opt/app/dist/server/main.js:2897:xx)
环境Libs:
"@angular/core": "^11.0.6",
"@ngxs/store": "^3.7.1",
"@ngxs-labs/select-snapshot": "^2.0.1",
Browser: only on SSR
这个问题的根本原因是这个文件:https://github.com/ngxs/store/blob/master/packages/store/src/decorators/select/select-factory.ts
存在于SelectFactory类中的存储键是整个服务器的单例(它不是Angular特有的单例)。你可以从它在装饰器中的使用方式中看到:https://github.com/ngxs/store/blob/8b52ae654c0a707612a8d23bde4737e69cbb8ee9/packages/store/src/decorators/select/symbols.ts#L11
因此,如果一个用户的NGXS模块在某个时候被销毁,而另一个用户的服务器端代码仍在运行,则出现错误
https://user-images.githubusercontent.com/37243777/109517260-bb977f00-7ab1-11eb-9b6c-7c92344b2465.png
我建议重构这个装饰器,让它依赖于所使用的每个组件中的Store,而不是依赖于整个服务器的单例Store。
同样,这个问题也存在于select-snapshot存储库中,并且与服务器范围内的singletone (https://github.com/ngxs-labs/select-snapshot)存在相同的潜在问题