0Pricing
Angular Academy · 课时

服务中的 SignalStore

在基于信号的服务中保存应用状态

服务中的 SignalStore 是 CoddyKit 上的免费 Angular Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Angular Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Angular Academy 课程共包含 4 节课。

作为状态的信号

Angular 的信号是具有响应性的值,在发生变化时会通知使用者。信号保存一个值,并会重新运行读取它的计算或模板。

将信号存储在 @Injectable 服务中,可以让多个组件共享同一份响应式状态。

最小信号存储

创建一个服务,并将状态保存在 signal() 中。将可写信号保持为私有,并公开一个只读视图。

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

@Injectable({ providedIn: 'root' })
export class CounterStore {
  private _count = signal(0);
  readonly count = this._count.asReadonly();

  increment() { this._count.update(c => c + 1); }
}

为什么使用 providedIn root

providedIn: 'root' 会让服务成为整个应用中的单例。每个注入该服务的组件都会共享完全相同的信号实例,因此状态始终保持同步。

读取信号

像调用函数一样调用信号,即可读取其值。在模板中,Angular 会跟踪读取操作,并在值发生变化时自动更新 DOM。

import { Component, inject } from '@angular/core';
import { CounterStore } from './counter.store';

@Component({
  selector: 'app-counter',
  template: '<button (click)="store.increment()">{{ store.count() }}</button>'
})
export class CounterComponent {
  store = inject(CounterStore);
}

asReadonly 保护状态

asReadonly() 会返回一个可读取但不可写入的信号。组件无法意外地对其调用 set() 或 update(),因此所有状态变更都会通过存储方法进行。

set 与 update

如果您已经直接得到下一个值,请使用 set(value)。如果下一个值取决于当前值,请使用 update(fn)。

private _user = signal<User | null>(null);

setUser(u: User) { this._user.set(u); }
clearUser() { this._user.set(null); }
touch() { this._user.update(u => u ? { ...u, seen: true } : u); }

每个存储包含多个信号

一个存储可以包含多个信号,分别表示状态的不同部分。将每个可写信号保持为私有,并公开相应的只读信号。

private _items = signal<Item[]>([]);
private _loading = signal(false);

readonly items = this._items.asReadonly();
readonly loading = this._loading.asReadonly();

方法封装逻辑

业务规则应位于存储方法中,而不是组件中。组件调用能够表达意图的方法,例如 addToCart(item),由存储决定状态如何变化。

addToCart(item: Item) {
  this._items.update(list => [...list, item]);
}
removeFromCart(id: string) {
  this._items.update(list => list.filter(i => i.id !== id));
}

无需订阅

与基于 RxJS 的服务不同,信号存储不需要 subscribe(),也不需要手动取消订阅。在模板中读取信号即可建立响应性连接,而组件销毁时 Angular 会自动清理这些连接。

使用 inject() 注入

inject() 函数可以在构造函数、字段初始化器和工厂函数中使用。它是获取存储实例的现代方式。

export class CartComponent {
  private store = inject(CartStore);
  items = this.store.items; // read-only signal
}

测试信号存储

信号存储就是普通类,因此测试很简单:创建实例,调用方法,然后同步读取信号值。无需异步处理。

it('increments', () => {
  const store = new CounterStore();
  store.increment();
  expect(store.count()).toBe(1);
});

快速检查

测试您对信号存储的理解。

回顾

您学会了构建基于信号的存储:将可写信号保持为私有,公开 asReadonly() 视图,在方法中通过 set/update 修改状态,并使用 inject() 注入单例。无需订阅,易于测试,还能共享响应式状态。

常见问题解答

「服务中的 SignalStore」课时是免费的吗?

是的 — 「服务中的 SignalStore」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Angular Academy 课程的其余内容,请升级到 CoddyKit PRO。 Angular Academy 课程共包含 4 节课。

「服务中的 SignalStore」这节课中我会学到什么?

在基于信号的服务中保存应用状态 你通过在浏览器中直接运行的动手代码来练习 Angular Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Angular Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Angular Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「服务中的 SignalStore」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Angular Academy 课中编写并运行代码吗?

能。每节 Angular Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 服务中的 SignalStore
  2. 使用 computed 派生状态
  3. 以不可变方式更新状态
  4. 连接信号与 RxJS
← 返回 Angular Academy