Observable vs Signal 比較
Angular 提供了兩種主要的反應式程式設計模式:傳統的 RxJS Observable 和 Signal。
基本概念
定義
隨時間流動的值 (values over time)
容器中的值 (values in container)
特性
需要訂閱(subscribe)才能獲取數據流通知
直接讀取值,無需訂閱
初始化
不一定需要初始值
必須提供初始值
適用場景
處理事件和流式數據(如HTTP請求、用戶輸入事件等)
管理應用狀態,UI渲染中的狀態管理
使用方式對比
Observable 使用方式
import { Observable } from 'rxjs';
// 創建Observable
const counter$ = new Observable<number>(subscriber => {
let count = 0;
const intervalId = setInterval(() => {
subscriber.next(count++);
}, 1000);
// 清理函數
return () => clearInterval(intervalId);
});
// 使用Observable(必須訂閱)
const subscription = counter$.subscribe({
next: (value) => console.log(`計數值: ${value}`),
error: (err) => console.error(err),
complete: () => console.log('完成')
});
// 需要手動取消訂閱
subscription.unsubscribe();
Signal 使用方式
import { signal, computed, effect } from '@angular/core';
// 創建Signal(必須有初始值)
const count = signal(0);
// 創建衍生Signal
const doubledCount = computed(() => count() * 2);
// 讀取Signal值(直接調用,無需訂閱)
console.log(`當前計數: ${count()}`);
console.log(`雙倍計數: ${doubledCount()}`);
// 更新Signal值
count.set(5); // 直接設置新值
count.update(val => val + 1); // 基於當前值更新
// 自動響應Signal變化
effect(() => {
console.log(`計數已更新為: ${count()}`);
document.title = `計數: ${count()}`;
}); // 自動在組件銷毀時清理
主要差異
執行模型
推送模型 (push-based)
拉取模型 (pull-based)
訂閱管理
需要手動管理訂閱
不需要訂閱管理
初始值
可選的
必須的
清理
手動 (unsubscribe)
自動 (在effect中)
複雜性
較高,有學習曲線
較低,API簡單
延遲執行
是(惰性執行)
否(立即執行)
操作符
豐富 (map, filter等)
較少 (update, set)
異步處理
天生支持
需要額外處理
使用案例比較
適合 Observable 的場景
HTTP請求:處理非同步數據獲取和錯誤處理
this.http.get<User[]>('/api/users') .pipe( retry(3), catchError(err => of([])) ) .subscribe(users => this.users = users);
事件處理:特別是需要防抖、節流等處理的事件
fromEvent(searchInput, 'input').pipe( debounceTime(300), map(e => (e.target as HTMLInputElement).value), distinctUntilChanged() ).subscribe(term => this.search(term));
複雜數據轉換:需要多步驟處理的數據流
this.userService.getUsers().pipe( map(users => users.filter(u => u.active)), switchMap(users => this.getPermissions(users)), shareReplay(1) ).subscribe(result => this.processResult(result));
適合 Signal 的場景
UI狀態管理:直接反映在模板中的數據
// 組件中 count = signal(0); increment() { this.count.update(c => c + 1); } // 模板中 <button (click)="increment()">增加</button> <p>計數: {{ count() }}</p>
衍生數據:基於其他狀態計算的值
price = signal(100); quantity = signal(2); // 自動計算總價 totalPrice = computed(() => this.price() * this.quantity()); // 稅後價格 taxedPrice = computed(() => this.totalPrice() * 1.1);
表單控制:直接與表單狀態同步
userName = signal(''); userEmail = signal(''); updateForm(form: {name: string, email: string}) { this.userName.set(form.name); this.userEmail.set(form.email); }
相互集成
實際上,Observable 和 Signal 可以相互配合使用,發揮各自的優勢:
// Observable 轉 Signal
import { toSignal } from '@angular/core/rxjs-interop';
const users$ = this.http.get<User[]>('/api/users').pipe(
shareReplay(1)
);
// 轉換為 Signal (可設定初始值)
const users = toSignal(users$, { initialValue: [] });
// 在模板中使用
// <li *ngFor="let user of users()">{{ user.name }}</li>
// Signal 轉 Observable
import { toObservable } from '@angular/core/rxjs-interop';
const counter = signal(0);
const counter$ = toObservable(counter);
// 使用 RxJS 操作符
counter$.pipe(
filter(count => count % 2 === 0),
map(count => `偶數: ${count}`)
).subscribe(message => console.log(message));
優缺點總結
Observable
• 功能豐富的操作符集合 • 出色的非同步處理能力 • 精細的錯誤處理 • 強大的流式資料處理能力 • 可組合性高
• 容易出現記憶體洩漏(忘記取消訂閱) • 除錯困難(錯誤可能被靜默吞噬) • 多層嵌套可能導致複雜性
Signal
• API 簡單直觀
• 不需要管理訂閱 • 自動與 Angular 變更檢測集成 • 使模板表達式簡潔 • 性能高效
• 處理異步邏輯較困難 • 操作符較少 • 用於複雜數據轉換時較為有限 • 相對較新,生態系統還在發展中
結論
選擇 Observable 還是 Signal 取決於具體需求:
如果處理事件流、HTTP請求、複雜的非同步操作,Observable 可能是更好的選擇
如果處理UI狀態、表單狀態、衍生數據,Signal 提供了更簡單、更直觀的方式
在Angular現代應用中,兩者常常配合使用:用Observable處理外部數據源和事件,然後轉換為Signal來管理組件的內部狀態。
Last updated