在前兩篇文章中,我們探討了:
接下來要討論的是狀態管理中最關鍵的問題:
👉 「狀態應該如何被修改?」
這個問題看似簡單,實際上卻是前端開發中容易踩坑的地方。
舉例來說:
要避免這些問題,我們需要建立一套 明確且可追蹤的狀態變更策略。
在 Angular 中,狀態的改變大致可以歸類為三種來源:
(click)
、(input)
HttpClient
回傳資料Router.events
監聽導航切換signal.set()
或 .next()
主動更新這三種來源涵蓋了絕大多數的狀態變化場景。下面我們就用 Angular 原生 API 來看看這些更新是怎麼發生的。
Angular 提供了非常直覺的事件繫結語法:
<button (click)="count = count + 1">增加</button>
<input (input)="onInput($event)" />
這種方式最簡單,直接在 Template 綁定事件就能改變狀態。
優點是快速、直觀;缺點是如果邏輯過多,容易分散在多處,不利集中管理。
當我們處理表單時,會常用到 FormControl
與 FormGroup
:
this.form = this.fb.group({
username: ['', Validators.required],
});
this.form.patchValue({ username: 'Zoe' });
這裡的 patchValue
就是一種 mutable 更新,直接修改表單內部的狀態模型。
優點是 API 完整,適合中大型表單;缺點是小表單會覺得有點繁瑣。
呼叫 API 是狀態更新的核心場景:
this.http.get('/api/orders').subscribe(res => {
this.orders = res; // 狀態更新
});
這裡的更新可以用 immutable(建立新陣列),也可以用 mutable(直接修改現有物件)。
導航事件也是一種狀態改變:
this.router.events.subscribe(e => {
if (e instanceof NavigationEnd) {
this.currentUrl = e.url;
}
});
這樣能在頁籤或側邊欄中即時反映目前所在位置。
Angular 16 推出的 Signals,是未來狀態管理的核心方向。
import { signal } from '@angular/core';
count = signal(0);
increase() {
this.count.set(this.count() + 1);
}
這裡的 set
就是狀態變更。Signals 自動追蹤依賴,會讓使用的地方自動更新,省去大量手動處理。
Angular 本身大量使用 RxJS,因此 Subject
也是常見的狀態更新工具:
private userSubject = new BehaviorSubject<User | null>(null);
user$ = this.userSubject.asObservable();
updateUser(user: User) {
this.userSubject.next(user);
}
這裡呼叫 .next()
,所有訂閱者就會收到更新。
適合事件驅動、跨元件通知、即時資料流。
類別 | Angular 工具 / API | 更新方式 | 策略 (Mutable / Immutable) | 適用場景 |
---|---|---|---|---|
使用者操作觸發 | Template Binding(click) , (input) |
直接在事件中修改狀態 | Mutable | 表單輸入、按鈕操作 |
Reactive Forms APIsetValue , patchValue |
呼叫表單方法更新模型 | Mutable | 登入、註冊、訂單編輯 | |
系統事件觸發 | HttpClient | API 回應後指派值 | 常用 Immutable(取代整個陣列/物件) | 清單載入、CRUD |
Router EventsNavigationEnd |
監聽路由事件後更新狀態 | Mutable 或 Immutable | 分頁切換、導航紀錄 | |
集中式更新 | Signals (Angular 16+)signal.set() |
更新 signal 狀態容器 | Mutable | UI 切換、輕量共享狀態 |
RxJS Subject / BehaviorSubject.next() |
推送新值給所有訂閱者 | 通常 Immutable | 即時資料、跨元件通知 | |
狀態策略 | 展開運算子 / map / filter | 建立新物件或新陣列 | Immutable | 清單操作、OnPush 變更檢測 |
直接修改屬性obj.name = 'Zoe' |
原地改動 | Mutable | 單純 UI 狀態、表單控制 |
前面提到的每一種更新方式,都可以再進一步區分成 mutable 或 immutable 兩種策略。
直接改變既有的物件或陣列:
this.form.patchValue({ name: 'Zoe' }); // 改變 FormControl 狀態
this.count.set(5); // Signals set
建立一份新物件或陣列,取代舊的:
this.orders = [...this.orders, newOrder]; // 建立新陣列
const updated = { ...this.user, name: 'Zoe' };
OnPush
變更檢測更穩定這一篇我們回答了第三個問題:
👉 「資料什麼時候、怎麼被改變?」
整理如下: