为了账号安全,请及时绑定邮箱和手机立即绑定

Angular信号:从零到英雄

反应式编程已成为现代前端开发的核心范式,框架如 Angular 也大量依赖于 Observables、EventEmitters 和状态管理。然而,随着 Angular 引入了 Signals,它提供了一种更简单、更直观的方式来管理本地状态。
在本文中,我们将从基础知识到高级用例详细介绍 Angular 中的 Signals,解释它们的工作原理,与可观察对象有何不同,以及如何使用它们构建高性能的 Angular 应用程序。

什么是 Angular 信号呢?

Angular 信号是反应式值,当它们的依赖项发生变化时会自动更新自己。你可以把它们想象成反应式变量:它们轻量、同步且易于使用,非常适合管理组件的局部状态。与通常用来处理异步数据流的可观察对象不同,信号是同步的,并且与 Angular 的变更检测机制非常兼容。

在 Angular 中,一个信号量表示一个会随时间变化的值,其他信号量可以依赖于它。这在你的应用程序中形成了自然的反应式状态管理流程,其中更新会自动传播开。

例子:

    import { signal } from '@angular/core';

    // 定义一个信号量
    const count = signal(0);

    // 更新信号量的值
    count.set(1);

    // 读取信号量的值
    console.log(count()); // 打印: 1

点击全屏模式。退出全屏。

信号和可观察对象的区别:它们有何不同

尽管“observables”和“signals”都是反应式编程结构,但它们在用途和行为上存在显著差异。

特性 信号 可观察对象
推送 vs 拉取(推 vs 拉) 拉取式(按需获取) 推送式(值被发出)
同步 同步且即时 可以是异步
订阅 不需要 需要
使用场景 本地状态 流和异步操作

虽然可观察对象更适合适用于事件流(如 HTTP 请求、WebSocket 消息),信号在处理本地、同步状态时更出色,这种状态在 Angular 组件内部需要被访问和更新。

在 Angular 中配置信号

要开始在 Angular 中使用信号,请确保你正在使用的是 Angular 16 或更高版本,因为这是信号功能已经完全集成到 Angular 核心中的版本。以下是一个基本的 Angular 组件中使用信号的示例:

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="增加()">点击增加</button>
    <p>计数: {{ count() }}</p>
  `
})
export class CounterComponent {
  count = signal(0);

  增加() {
    this.count.set(this.count() + 1);
  }
}

全屏 退出全屏

高级概念:衍生信号与计算信号

Angular 让你可以创建 衍生信号(这些信号依赖于其他信号),当其依赖发生变化时会自动更新。这使得处理复杂的状态依赖变得更容易。

派生信号

派生信号非常适合用来处理依赖其他信号计算得出的值。
比如,这里有一个简单的创建派生信号的例子:

    const baseCount = signal(10);
    const doubleCount = computed(() => baseCount() * 2);

    console.log(doubleCount()); // 输出结果为: 20

点击全屏 点击退出全屏

在这个例子中,doubleCount 总是返回 baseCount 值的两倍。如果 baseCount 有变化,doubleCount 会自动跟着变。

计算生成的信号:

你也可以创建计算生成的信号,它们类似于派生信号,但当你需要进行更复杂的计算时,可以使用它们。

/**

* 下面是一个简单的计算示例。

* 这里我们使用了 `signal` 和 `computed` 来计算两个信号的和。
 */
const a = signal(5);
const b = signal(3);
const sum = computed(() => a() + b());

console.log(sum()); // 控制台输出:8

全屏显示 退出全屏

每当 ab 变化时,sum 值会自动更新。

Angular 变化检测中的信号整合

信号的一个关键优势是它们与 Angular 的变更检测机制深度整合。信号会自动触发视图更新,即使在使用 OnPush 策略时也不例外。这显著提升了性能,而无需手动调用变更检测。

关于 OnPush 的示例:

    @Component({
      selector: 'app-signal-example',
      template: `<p>{{ 全名() }}</p>`,
      changeDetection: ChangeDetectionStrategy.OnPush
    })
    export class SignalExampleComponent {
      firstName = signal('Soumaya');
      lastName = signal('Erradi');
      全名 = computed(() => `${this.firstName()} ${this.lastName()}`);
    }

全屏显示,退出全屏

即使启用了OnPush,当firstNamelastName(即名字或姓氏)发生变化时,视图依然会更新,这是由于信号的响应特性。

复杂应用中的信号处理技术

在更复杂的场景下,例如管理整个应用程序的状态,信号可以简化状态管理,通过明确声明各个部分之间的依赖关系。例如,在购物车应用中,你可以利用信号来管理购物车中的商品,并根据添加的商品自动计算总价。

例子:带有信号指示的购物车

    @Injectable({ providedIn: 'root' })
    export class 购物车服务 {
      private items = 信号<购物车项[]>([]);

      添加购物车项(item: 购物车项) {
        this.items.set([...this.items(), item]);
      }

      获取总价 = 计算(() =>
        this.items().reduce((total, item) => total + item.price, 0)
      );
    }

全屏模式,退出全屏

每当购物车中的商品被添加或移除时,getTotalPrice 信号会自动更新。

信号的最佳做法

在使用Angular应用中的信号时,以下是一些最佳实践,以实现最佳性能和可维护性:

  • 使用信号来管理本地和同步状态:信号非常适合管理组件状态或组件内的任何同步数据依赖。
  • 保持衍生信号简单:虽然衍生信号很有用,但尽量减少依赖关系,避免过于复杂的信号链,这会使调试变得复杂。
  • 利用自动更新功能:不要通过手动跟踪状态更新来使基于信号的组件过于繁琐。让 Angular 自动处理状态更新。
  • 结合信号和可观察对象:在某些情况下,你可能仍然需要使用可观察对象来管理异步数据。在这种情况下,使用信号来管理同步状态,使用可观察对象来处理异步数据流。

总结

Angular Signals 引入了一种新的方法来管理 Angular 应用程序中的反应式状态。它们提供了一种轻量级、同步且声明式的方式来管理状态,使您的应用程序更容易理解和推理,并且更易于维护。随着您构建更复杂的应用程序,信号可以减少样板代码的需求,消除手动订阅的需求,并简化您的变更检测。

有了这些基础知识,你就可以开始利用信号构建更高性能、更灵敏地响应的Angular应用程序了。

编程开心!

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消