首页  >  文章  >  web前端  >  Angular 反应性的下一个改进

Angular 反应性的下一个改进

Mary-Kate Olsen
Mary-Kate Olsen原创
2024-11-17 22:19:01184浏览

The next improvement in Angular reactivity

介绍

自 Angular 最新版本以来,在框架内开发了一种新的原始反应系统:信号!

今天,事后看来,我们意识到某些用例尚未被覆盖,显然 Angular 团队的积极反应将为我们提供帮助来覆盖这些用例。

这些用例是什么?将采取哪些解决方案以及如何使用它们?

相对于另一个信号重置一个信号的过程

让我们首先说明这个问题。

假设我们有一篮子一定数量的水果。
数量由输入水果的组件管理。

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}

如果输入的水果价格发生变化,则必须重置变量。

一个简单的解决方案是使用效果

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = signal(1);

  countEffect(() => {
    this.fruit();
    this.quantity.set(1);
  }, { allowSignalWrites: true })

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}

前面的代码是不好的做法。为什么这是一个大问题?

我们需要将 signalWrites 选项设置为 true 才能设置信号量。这是由于对给定问题的误解造成的。
在我们的例子中,我们想要同步两个变量,在我们的具体化中,这两个变量是不同步的

柜台并不独立于水果,水果是我们最初的来源。实际上,这里我们有一个组件状态,其初始来源是水果,其余部分是水果的衍生物。

具体化问题如下

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{fruitState().quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();

  fruitState = computed(() => ({
    source: fruit(),
    quantity: signal(1),
  }));

  updateQuantity(): void {
    this.fruitState().quantity.update(prevCount => prevCount++);
  }
}

这种具体化将水果与其数量紧密联系起来。
因此,一旦水果发生变化,计算变量fruitState就会自动重新计算。此重新计算返回一个具有数量属性的对象,该对象是一个初始化为 1 的信号。

通过返回信号,变量可以在点击时递增,并在水果变化时简单地重置。

这是一个相对简单的设置模式,但我们不能简化它吗?

LinkedSignal 函数可以解决这个问题。

随着 Angular 19 的到来,一个用于计算派生信号的新函数。

到目前为止,我们已经有了计算函数,但该函数返回一个 Signal 而不是 WrittableSignal,这在我们之前的数量变量用例中是实用的。

这就是 LinkedSignal 的用武之地。LinkedSignal,顾名思义,允许您将两个信号强链接在一起。

如果我们回到之前的情况,这个函数将允许我们简化代码,如下所示:

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = linkedSignal({ source: fruit, computation: () => 1 });

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}

linkedSignal函数定义如下:

linkedSignal(computation: () => D, options?: { equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

linkedSignal(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: NoInfer<D>; }) => D; equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

在第一个定义(“缩写”定义)中,linkedSignal 函数采用计算函数作为参数和配置对象。

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}

在前面的示例中,由于计算函数取决于数量信号,因此当数量变化时,计算函数会重新评估。

在第二个定义中,linkedFunction方法将一个对象作为具有三个属性的参数

  • 来源:计算函数重新评估所依据的信号
  • 计算函数
  • 参数对象

与“缩写”计算函数相反,这里的计算函数将源值和“先例”作为参数。

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = signal(1);

  countEffect(() => {
    this.fruit();
    this.quantity.set(1);
  }, { allowSignalWrites: true })

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}

新的资源 API

Angular 19 将引入一个新的 API,用于简单的数据获取和查询状态(待处理等)、数据和错误的检索。

对于那些稍微熟悉框架的人来说,这个新 API 的工作方式有点像 useRessource 钩子。

让我们看一个例子:

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{fruitState().quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();

  fruitState = computed(() => ({
    source: fruit(),
    quantity: signal(1),
  }));

  updateQuantity(): void {
    this.fruitState().quantity.update(prevCount => prevCount++);
  }
}

关于此代码片段,有几件事需要了解

这段代码中有几点需要注意:

  • 默认情况下,加载器将 Promise 作为参数
  • fruitRessource类型是WrittableRessource,这将使我们能够根据需要在本地修改数据
  • 水果详细信息的请求在资源fuitResource创建时直接发送到服务器
  • this.fruitId() 在加载器中未跟踪,因此如果 FruitId 更改,则不会发送新请求
  • WrittableRessource有一个方法refresh来刷新数据

以下效果将打印这些值

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = linkedSignal({ source: fruit, computation: () => 1 });

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}

如上所述,默认情况下,fruitId 信号是未跟踪的。

那么每次这个信号的值发生变化时如何重新启动http请求,以及如果fruitId信号的值发生变化而对上一个请求的响应没有变化,如何取消上一个请求到了吗?

资源函数采用另一个名为 request 的属性。

此属性将依赖于信号的函数作为其值并返回其值。

linkedSignal(computation: () => D, options?: { equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

linkedSignal(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: NoInfer<D>; }) => D; equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

如上面的代码所示,加载器函数有两个参数

  • 请求值:这里是fruitId信号的值
  • 用于取消 http 请求的 abortController 对象的 signal 属性值。

因此,如果在 httpRequest 检索水果详细信息期间,fruitId 信号的值发生变化,则该请求将被取消以启动新请求。

最后,Angular 还想到了将这个新 api 与 RxJs 耦合的可能性,让我们能够从 Rx 运算符的强大功能中受益。

互通性是通过 rxResource 函数实现的,它的定义方式与资源函数完全相同。
唯一的区别是 loader 属性的返回类型,它将返回一个 observable

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}

这里不需要abortSignal,当信号fruitId的值改变时取消之前的请求,隐含在函数rxResource中,行为与switchMap操作符相同。

以上是Angular 反应性的下一个改进的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn