基礎 signal

Angular 16 加入了 signal 功能,在 Angular 17 部分穩定。

signal 是一個帶值的容器,當容器內的值發生改變時會通知 Angular,Angular 隨即更新所有使用到此 signal 的 UI 部分。此概念類似 Vue.js 的 ref。

基本用法

  1. 透過 () 設定預設值:

selectedUser = signal(DUMMY_USERS[randomIndex]);
  1. 透過 .set 改變值:

this.selectedUser.set(DUMMY_USERS[randomIndex])

在 HTML 中使用

在 html 中不能作為屬性或變數調用,需作為函數調用以取得最新值(類似 Vue.js 的 ref 需透過 .value 取值)。

一般寫法:

<span>{{ selectedUser.name }}</span>

signal 寫法:

<span>{{ selectedUser().name }}</span>

此寫法通知 Angular 在 template 中需要此 signal 的最新值。Angular 於背後執行訂閱,確保值改變時重新渲染 UI。

Zone.js vs Signal

未使用 signal 時,Angular 採用 Zone.js sub-package。它於 Angular 背後設置不可見的分組機制,監聽可能觸發狀態改變的事件。事件觸發時(如用戶點擊按鈕),Angular 遍歷 zone 中的所有 component,確認數據變化並更新 UI。

signal 使 Angular 能避免使用 zone 機制,提升效率。signal 使 UI 能以更精確的方式更新,無需檢查所有可能的事件。

Computed

getter 可改用 computed(computed 為配合 signal 使用的函數)

一般寫法:

get imagePath() {
  return 'assets/users/' + this.selectedUser().avatar;
}

使用 computed:

imagePath = computed(() => {
  return 'assets/users/' + this.selectedUser().avatar;
});

使用時需作為函數調用,加上 ()

<img [src]="imagePath()" [alt]="selectedUser().name">

computed 的優點

Angular 會分析 computed 函數內使用的 signal 值,並向該 signal 訂閱。signal 值變化時執行重新計算,提升效率。不會在每次 component 或 app 變化時重新計算,僅在使用的 signal 改變時執行計算。

提問:signal 的訂閱機制是否比 zone 的遍歷方式更耗效能?

Zone.js vs Signal 效能比較

特性
Zone.js
Signal

監聽範圍

所有變更事件

實際使用的值

檢查機制

遍歷所有組件

直接通知相關 UI

更新範圍

可能更新整個應用

僅更新相關部分

計算成本

每次事件均需遍歷

僅在值改變時計算

結論:Signal 的訂閱機制比 Zone.js 的全面檢查更有效率。初始雖需建立訂閱關係,但執行時僅處理需要更新的部分,避免不必要的檢查與計算。

Last updated