gpt4 book ai didi

angular - 非平凡 Angular 组件中 RxJS 的最佳实践

转载 作者:太空狗 更新时间:2023-10-29 17:20:48 25 4
gpt4 key购买 nike

我们的团队正在从 AngularJS 迁移到 Angular (4)。

在我们的 Angular 组件中,我们使用 RxJS 库来处理从 Http、ActivatedRoute 等服务返回的可观察对象或从用户界面上的输入接收值的主题。当需要对这些 observable 产生的值进行某些处理时,我们将它们映射或组合到新的 observable 中以产生“派生”的 observable。这里没有什么特别的。

我们最终得到的组件几乎所有从模板绑定(bind)的公共(public)属性都是可观察的。

但是,这种技术似乎有很大的缺点:

  • 它需要对 RxJS 库有很好的了解和理解,许多以前看似简单的任务变得非常复杂,需要花费大量时间调试/优化可观察对象订阅。
  • 我们的模板现在充满了异步管道,我们经常会遇到这样的 Angular 错误:https://github.com/angular/angular/issues/10165 .

举个例子,这是一个简单的案例(到目前为止还不是最糟糕的):分页组件的模板。请注意大量的异步管道,其中一些嵌套在结构指令中,本身是异步的(这导致了我刚才提到的错误)。

<nav aria-label="Pagination" class="mt-5" *ngIf="($items | async)?.length">
<ul class="pagination">
<li class="page-item" [class.disabled]="(currentPage$ | async) === 1">
<a class="page-link" [routerLink]="" [queryParams]="{ page: (currentPage$ | async) - 1 }" queryParamsHandling="merge">Previous</a>
</li>
<li *ngFor="let page of (pages$ | async)" class="page-item" [class.active]="page === (currentPage$ | async)">
<a class="page-link app-page-number" [routerLink]="" [queryParams]="{ page: page }" queryParamsHandling="merge">{{ page }} <span class="sr-only" *ngIf="page === (currentPage$ | async)">(current)</span></a>
</li>
<li class="page-item" [class.disabled]="(currentPage$ | async) === (lastPage$ | async)">
<a class="page-link" [routerLink]="" [queryParams]="{ page: (currentPage$ | async) + 1 }" queryParamsHandling="merge">Next</a>
</li>
</ul>
</nav>

将“派生的”可观察对象更改为简单的(不可观察的)属性,从 .subscribe() 填充有助于降低代码复杂性,以及模板内异步管道的数量。但这并不是一个令人满意的解决方案。

此外,我们还遇到了一些与 OnPush 策略的变化检测相关的新问题。由于我们将 observables 变成了“普通”属性并删除了异步管道,Angular 不再知道我们正在更改这些属性并且我们经常不得不调用 ChangeDetectorRef.detectChanges()。

到目前为止,我们还没有找到关于如何在非平凡组件中处理 RxJS 的明确最佳实践。您对如何解决这些困难有什么建议吗? Redux 可以解决(某些)我们的问题吗?

编辑:事实证明,上述“错误”实际上是我们对 RxJS 库的误用。有关详细信息,请参阅我对 ( GitHub issue) 的贡献。

最佳答案

变化检测

*ngIf="(obs | async)" 的问题是变更检测之一。变量 obs 在发射时本身不会改变,加上它是模板中的表达式,使得变化检测很难检测到。

在这种情况下有几个原则值得考虑:

  • 可观察变量是“管道”,即通过它们传递的值的包装器,而不是变化的值本身。这两者经常被等同起来,但这类似于认为数组和数组元素是同一件事。

  • RxJs 是一个“外部”(非 Angular)库。对于这些库,我们需要了解库处理的数据更改是否对 Angular 更改检测可见。

#10165 的修复是

<div style="background-color: green;" *ngIf="trigger">{{(val1 | async)}}</div>
<div style="background-color: green;" *ngIf="!trigger">{{(val2 | async)}}</div>

ngOnInit() {
this.trigger = this.ifObservable.subscribe();
}

处理异步数据

关于“无处不在的可观察对象”这一更广泛的问题,您是完全正确的,但这不是网络应用程序的异步特性固有的问题吗?

将旧的 AngularJS 代码与新的 Angular 代码进行对比会很有趣。同一套功能实现的复杂性是否有所增加?

我喜欢应用的一个原则是通过订阅它们来处理诸如 http.get 之类的“一次性”可观察对象,并通过异步管道保持“多次”可观察对象的连接。

OnPush

这本质上是一种减少正在进行的自动更改检测数量的方法,从而加快应用程序的速度。当然,这意味着您可能不得不更频繁地手动进行火灾变化检测——速度与复杂性之间的权衡。

终极版

虽然 Redux 存储通常将状态公开为可观察对象(因此您仍然需要模板中的异步管道),但它确实消除了围绕多个状态更新点的复杂性,这可能意味着需要更少的可观察对象(或者至少可观察对象是抽象的)远离组件)。

我看过的唯一不涉及在模板中取消异步数据的 Redux 风格是 Mobex ,它本质上是将简单变量转换为具有 getter 和 setter 的对象,其中包含监视异步更改并解包它们的逻辑。但它对数组也有问题/警告。

简化模板

可能简化您显示的模板的一种方法(我还没有测试过)是将其包装在子组件中并传入未包装的值

<nav aria-label="Pagination" class="mt-5" *ngIf="items.length">
<ul class="pagination">
<li class="page-item" [class.disabled]="currentPage === 1">
<a class="page-link" [routerLink]="" [queryParams]="{ page: currentPage - 1 }" queryParamsHandling="merge">Previous</a>
</li>
<li *ngFor="let page of pages" class="page-item" [class.active]="page === currentPage">
<a class="page-link app-page-number" [routerLink]="" [queryParams]="{ page: page }" queryParamsHandling="merge">{{ page }} <span class="sr-only" *ngIf="page === currentPage">(current)</span></a>
</li>
<li class="page-item" [class.disabled]="currentPage === lastPage">
<a class="page-link" [routerLink]="" [queryParams]="{ page: currentPage + 1 }" queryParamsHandling="merge">Next</a>
</li>
</ul>
</nav>


export class PageComponent {

@Input() currentPage = 0;
@Input() pages = [];
@Input() items = [];

在父级中,

<page-component [pages]="pages$ | async" [items]="items$ | async" [currentPage]="currentPage$ | async" >

从本质上讲,@Input 与变更检测 Hook ,即使使用 onPush 策略也是如此。
小心给子输入默认值。

关于angular - 非平凡 Angular 组件中 RxJS 的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47081183/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com