我遇到了一个奇怪的问题,我希望这对我来说只是一个简单的修复。 Angular 中的变更检测不仅仅在生产中起作用。
我使用 Angular 7 和 ngrx 作为状态管理构建了一个相当复杂的应用程序。有 2 个主要模块是基于路由进行延迟加载的。更改检测策略是 OnPush,99% 的应用程序遵循以下模式:数据通过 ngrx 效果从服务器发送到有状态组件,然后通过异步管道将数据推送到“虚拟”表示组件中。 示例:
this.loading$ = this.store.pipe(
select(selector.isLoading),
takeUntil(this.unsubscribe$)
);
<app-loading [loading]="loading$ | async"></app-loading>
在
ng serve
和 ng serve --prod
上一切都按预期进行,没有任何问题。然而,在运行 ng build --prod
并将 js 文件推送到我们的 .net 解决方案中后,更改检测将停止工作。
“停止工作”是什么意思?我的意思是,除非发生 DOM 事件,否则它不会更改任何模型或组件。
例如,使用上面显示的代码,会显示一个加载旋转器,直到数据来自服务器为止。在生产中,旋转器位于屏幕上,直到您随机单击。然后应用程序刷新并显示数据。这个问题在整个应用程序中持续存在。也就是说,这个问题不仅仅发生在那里。
应用程序加载到 .Net 解决方案中的 .cshtml 文件中,如下所示:
<script type="text/javascript" src="runtime.js"></script>
<script type="text/javascript" src="polyfills.js"></script>
<script type="text/javascript" src="main.js"></script>
延迟加载的模块位于 .cshtml 文件的文件夹内,并且没有显示任何错误,因此我必须假设所有内容都已正确连接。
同样,这仅在产品包中。不在开发或服务或生产模式服务。
这最终导致了来自操作员的 rxjs 的一个非常奇怪的情况。
应用程序使用signalR作为客户端和服务器之间的API。与集线器的大部分交互都是 jquery 承诺。我使用 from 运算符返回数据。
return from(this.proxy.invoke(...args));
由于我无法弄清楚的原因,这在开发中很有效。但观察者不会在生产中完成,因此即使状态会更新,视图仍在等待观察者完成。
我更新了代码以在连接上返回可观察值。
private async performInvoke(...argu): Promise {
return await this.proxy.invoke(...argu);
}
invoke(...argu): Observable<any> {
return new Observable(o =>
const pInvoke = async () => {
o.next(await this.performInvoke(...argu));
o.complete();
}
}
我在这里进行了简化,因此如果您发现这一点,请确保添加尝试捕获和其他错误保护。
对于那些因不可靠的 ChangeDetection 面临类似问题的人:
我遇到过类似的问题,这只发生在定制的嵌入式 Chromium 中。
ChangeDetection 在本地主机环境中始终正常工作,但在通过 VNC 访问的嵌入式 Chromium 中并不可靠。即使二进制文件完全相同,应用程序的行为也不同,因此它一定是由浏览器中的某些内容引起的。
hacky 解决方案是通过
setInterval
强制进行更改检测,如下所示:
// AppComponent
private cdInterval = undefined;
ngOnInit() {
this.cdInterval = setInterval(() => {
this.cd.detectChanges();
}, 50);
ngOnDestroy() {
if (this.cdInterval) {
clearInterval(this.cdInterval);
}
}
这显然远非理想,但至少 UI 保持同步。
郑重声明: 我已经尝试了所有我能想到的:
为每个订阅调用
cd.detectChanges()
可以,但无法扩展,所以我选择间隔调用它。
我会非常高兴有更好的解决方案。