Angular 信号使用入门

1. 准备工作

黑色 Angular 徽标

Angular 信号向 Angular 带来了您熟知和喜爱的三种响应式基元,简化了您的开发工作并帮助您默认构建更快的应用。

构建内容

  • 您将了解 Angular 信号引入的三种响应式基元:signal()computed()effect()
  • 使用 Angular 信号为 Angular 加密游戏提供支持。加密算法是用于加密和解密数据的系统。在这个游戏中,用户可以通过拖放提示来破解密码,从而对提示消息进行解码,自定义该消息,分享网址并发送给好友。

复古复古游戏机风格的 Angular 赛博游戏,屏幕上显示一条隐藏的消息:“Anqnxaa Lpcnaxl aaf pn jfafxyofa aofapfm pn a16 wyjak!”

前提条件

2. 获取代码

此项目所需的所有信息都位于 Stackblitz 中。建议使用 Stackblitz 完成此 Codelab。作为一种替代方案,您可以克隆代码,然后在您喜爱的开发环境中将其打开。

打开 Stackblitz 并运行应用

首先,使用您喜爱的网络浏览器打开 Stackblitz 链接:

  1. 打开新的浏览器标签页,然后转到 https://stackblitz.com/edit/io-signals-codelab-starter?file=src%2Fcrypto%2Fservice.crypto.ts,src%2Fsecret-message%2Fservice.message.ts&service.massage.ts
  2. 复刻 Stackblitz,创建您自己的可修改工作区。Stackblitz 应该会自动运行该应用,您已准备就绪!

替代方案:克隆代码库并提供应用

使用 VSCode 或本地 IDE 是完成此 Codelab 的另一种方法:

  1. 打开新的浏览器标签页,然后转到 https://github.com/angular/codelabs/tree/signals-get-started
  2. 针对代码库创建分支和克隆,并使用 cd codelabs/ 命令移入代码库。
  3. 查看包含 git checkout signals-get-started 命令的起始代码分支。
  4. 在 VSCode 或您的首选 IDE 中打开代码。
  5. 如需安装运行服务器所需的依赖项,请使用 npm install 命令。
  6. 如需运行服务器,请使用 ng serve 命令。
  7. 打开一个浏览器标签页以访问 http://localhost:4200

3. 建立基准

您可以从 Angular 加密游戏入手,但它尚未成功。Angular 信号将为游戏功能提供支持。

复古复古游戏机风格的 Angular 赛博游戏,屏幕上显示一条隐藏的消息:“Anqnxaa Lpcnaxl aaf pn jfafxyofa aofapfm pn a16 wyjak!”

首先,我们来详细了解一下您将构建的几个已完成版本:Angular 信号循环

  1. 在屏幕上查看经过编码的消息。
  2. 按一下拨号键盘中的字母按钮,即可解开密码并解码这条秘密消息。
  3. 成功执行后,请查看消息如何更新以解码更多秘密消息。
  4. 点击自定义更改“发件人”和“邮件”,然后点击创建和复制网址以查看屏幕上的值和网址更改。
  5. 额外提示:将网址复制并粘贴到新标签页中,或与朋友分享,看看发送者和邮件在网址中的存储情况。

Angular Cypher 游戏的 GIF,屏幕上正在解码一条隐藏的消息,以拼出“Angular 信号目前在 v16 中的开发者预览版!

4. 定义您的第一个信号()

信号是一个值,可在 Angular 发生变化时告知它。有些信号可以直接更改,有些则根据其他信号的值计算得出。这些信号会共同形成一个定向的依赖关系图,从而模拟数据在应用中的流动方式。

Angular 可以利用信号中的通知来了解需要检测哪些组件或执行您定义的效应函数。

superSecretMessage 转换为 signal()

superSecretMessageMessageService 中的值,用于定义播放器解码的私密消息。目前,该值不会导致应用发生变化,因此自定义按钮已损坏。您可以通过信号解决此问题。

通过将 superSecretMessage 作为信号,您可以通知应用中了解消息何时发生更改的部分。自定义对话框中的消息时,您可以设置信号,以使用新消息更新应用的其余部分。

如需定义您的第一个信号,请在每个文件的 TODO(1): Define your first signal() 注释下执行以下步骤:

  1. service.message.ts 文件中,使用 Signals 库让 superSecretMessage 被动:

src/app/secret-message/service.message.ts

superSecretMessage = signal(
  'Angular Signals are in developer preview in v16 today!'
);

系统会自动提示您从 @angular/core 导入 signal。如果您刷新页面,很可能会遇到您之前引用了 superSecretMessage 的错误。这是因为您已将 superSecretMessage 的类型从 string 更改为 SettableSignal<string>。您可以通过将 superSecretMessage 的所有引用更改为使用 Signals API 来解决此问题。无论在何处读取该值,都调用信号获取方 superSecretMessage()。无论您在何处写入值,都可以使用 SettableSignal 上的 .set API 为消息设置新值。

  1. secret-message.tsservice.message.ts 文件中,将 superSecretMessage 的所有引用更新为 superSecretMessage()

src/app/secret-message/secret-message.ts

// Before
this.messages.superSecretMessage
this.messages.superSecretMessage = message;

// After
this.messages.superSecretMessage()
this.messages.superSecretMessage.set(message);

src/app/secret-message/service.message.ts

// Before
this.superSecretMessage

// After
this.superSecretMessage()

了解另外两个信号

  • 请注意,您的应用中还有其他两个信号:

src/app/crypto/service.crypto.ts

cipher = signal(this.createNewCipherKey());
decodedCipher = signal<CipherKey[]>([]);

CipherService 定义了一个 cipher 信号,这是随机生成的一个字母表与 1 个字母的 cipher 字母的映射。您可以使用此函数对消息进行加扰,并确定玩家是否在键盘上找到成功的匹配项。

此外,您还有 decodedCipher 信号(表示成功解码的键值对),您将在播放器解出密码时将其添加到该键值对中。

Angular 的信号设计具有独特且强大的属性,那就是您可以随时随地引入响应式。您在应用的服务中定义了一次信号,之后便可在模板、组件、管道、其他服务或您可以编写应用代码的任意位置使用这些信号。它们不受限制,也可以绑定到组件范围。

验证更改

  • 您还需执行一个步骤,该应用才能正常运行。现在,请尝试在应用的不同部分添加一个 console.log(),以查看新的 superSecretMessage 的设置方式。

显示包含 superSecretMessage 的 console.log() 消息的 Stackblitz。

5. 定义您的第一个 compute()

在许多情况下,您可能会发现自己会从现有值中派生状态。最好在依赖值发生变化时更新派生状态。

借助 computed(),您可以声明性地表达通过其他信号得出的值。

solvedMessage 转换为 computed()

solvedMessage 会使用 decodedCipher 信号将 secretMessage 值从编码转换为解码。

这太酷了,因为您看到自己是根据另一项计算衍生出的,因此每当映射的反应式上下文中的信号发生变化时,就会通知依赖项。

目前,当您更改 secretMessagedecodedCiphersuperSecretMessage 时,solvedMessage 不会更新。这样,当玩家解出密码时,您就不会看到屏幕更新。

通过计算 solvedMessage,您可以创建一个反应式上下文,以便在更新消息或解决加密时,可以从跟踪的依赖项派生状态更新。

如需将 solvedMessage 转换为 computed(),请在每个文件的 TODO(2): Define your first computed() 注释下执行以下步骤:

  1. service.message.ts 文件中,使用 Signals 库让 solvedMessage 被动:

src/app/secret-message/service.message.ts

solvedMessage = computed(() =>
  this.translateMessage(
    this.secretMessage(),
    this.cipher.decodedCipher()
  )
);

系统会自动提示您从 @angular/core 导入 computed。如果您刷新页面,很可能会遇到您之前引用了 solvedMessage 的错误。这是因为您已将 superSecretMessage 的类型从 string 更改为 Signal<string>。您可以通过将 solvedMessage 的所有引用更改为 solvedMessage() 来解决此问题。

  1. secret-message.ts 文件中,将 solvedMessage 的所有引用更新为 solvedMessage()

src/app/secret-message/secret-message.ts

// Before
<span *ngFor="let char of this.messages.solvedMessage.split(''); index as i;" [class.unsolved]="this.messages.solvedMessage[i] !== this.messages.superSecretMessage()[i]" >{{ char }}</span>

// After
<span *ngFor="let char of this.messages.solvedMessage().split(''); index as i;" [class.unsolved]="this.messages.solvedMessage()[i] !== this.messages.superSecretMessage()[i]" >{{ char }}</span>

请注意,与 superSecretMessage 不同,solvedMessage 不是 SettableSignal,您不能直接更改其值。但每当其依赖项信号(secretMessagedecodedCipher)之一进行更新时,其值都会保持最新。

探索另外两个 computed() 函数

  • 请注意,您的应用中还有另外两个计算值:

src/app/secret-message/service.message.ts

secretMessage = computed(() =>
  this.translateMessage(
    this.superSecretMessage(),
    this.cipher.cipher()
  )
);

src/app/crypto/service.crypto.ts

unsolvedAlphabet = computed(() =>
  ALPHABET.filter(
    (letter) => !this.decodedCipher().find((guess) => guess.value === letter)
  )
);

MessageService 定义由 secretMessage 计算的 superSecretMessage(由 cipher 编码),让玩家努力解决。

CipherService 会定义一个 unsolvedAlphabet,其中包含玩家尚未解决的所有字母的列表,该列表派生自 decodedCipher 中已解决的加密密钥列表。

验证更改

现在 superSecretMessage 是一个信号,且 solvedMessage 是一个计算完成,应用应该可以正常运行了!测试游戏的功能:

  1. LetterGuessComponent 拖放到 CipherComponent 中的 LetterKeyComponent,有助于解决加密问题和解码私密消息。
  2. 查看当您解码更多私密消息时 SecretMessageComponent 如何更新。
  3. 点击自定义更改“发件人”和“邮件”,然后点击创建和复制网址以查看屏幕上的值和网址更改。
  4. 额外提示:将网址复制并粘贴到新标签页中,或与朋友分享,看看发送者和邮件在网址中的存储情况。

Angular Cypher 游戏的 GIF,屏幕上正在解码一条隐藏的消息,以拼出“Angular 信号目前在 v16 中的开发者预览版!

6. 添加您的首个效果()

有时,您可能需要在信号具有新值时发生某些情况。借助 effect(),您可以安排并运行处理程序函数,以响应信号变化。

解开密码后添加五彩纸屑

现在,应用能够正常运行了,接下来您可以在加密算法解决和密码加密完成时添加一些五彩纸屑。

如需添加五彩纸屑,请在 TODO(3): Add your first effect() 注释下执行以下步骤:

  1. cipher.ts 文件中,设置在消息解码时添加五彩纸屑的效果:

src/app/crypto/crypto.ts

import * as confetti from 'canvas-confetti';

ngOnInit(): void {
  ...

  effect(() => {
    if (this.messages.superSecretMessage() === this.messages.solvedMessage()) {
      var confettiCanvas = document.getElementById('confetti-canvas');
      confetti.create()(confettiCanvas, { particleCount: 100 });
    }
  });
}

请注意此效果如何取决于信号和计算得出的值:this.messages.superSecretMessage()this.messages.solvedMessage()

效果可帮助您在反应式上下文中调度 confetti 函数,以便在其依赖项更新时进行跟踪和重新评估。

验证更改

  • 尝试解决加密问题(提示:您可以将消息更改为简短内容,以便更快地进行测试!)。五彩纸屑来袭,恭喜您完成了第一个effect()

《Angular Cypher》游戏的 GIF,画面中开始隐藏的一条消息,拼出“Confetti time!”(糖果时间!)的五彩纸屑,当破解完谜语后,爆米花将消失。

7. 恭喜!

现在,您的 Angular 加密工具可以解码并分享秘密消息了!对于 Angular 团队,您有消息吗?使用 @Angular 标记我们的社交媒体,以便我们对其进行解码!🎉

在 Angular “今天的 v16 中进行了开发者预览版!”屏幕上显示一条隐藏消息,以呈现 Angular Cypher 游戏!

现在,Angular 工具箱中有三个新的响应式基元,可以简化您的开发并默认构建更快的应用。

了解详情

查看以下 Codelab:

请阅读以下资料: