在這篇短文中,我想向您展示我如何使用訊號建立我的元件,而不需要外部函式庫。當然,像 NgRx 這樣的東西將發揮巨大的作用,使我們的程式碼更加健壯,但讓我們從簡單的開始!
首先,我用訊號定義所有狀態:
export class TodoListComponent { todos = signal<Todo[]>([]); }
這也適用於輸入!如果我的元件需要輸入,我會使用 Angular 的新 input() 函數來宣告它,這也會給我一個訊號。如果它剛好是路由參數,我使用 input.required().
然後,如果我想顯示可以從另一個狀態匯出的某種狀態,我總是使用計算:
completedTodos = computed(() => this.todos().filter(t => t.completed));
然後,如果你了解我,你就知道我有多鄙視直接在類別方法中執行非同步副作用......?
export class TodoListComponent { todoService = inject(TodoService); toggleTodo(id: string) { this.todoService.toggle(id).subscribe(newTodo => ...); } }
為什麼這麼問?因為如果該方法是直接引發副作用的方法(在本例中為呼叫 subscribe),則您無法控制背壓。
背壓可以總結為:如果使用者在上一個呼叫尚未完成時切換待辦事項,會發生什麼事?
有很多問題,例如:
如果您了解 RxJS(如果您正在閱讀本文,那麼您現在應該知道了!)您知道第一個問題可以透過 4 個扁平化運算子(mergeMap、concatMap、switchMap、exhaustMap)輕鬆解決。
然後,如果你非常了解 RxJS,你就會知道你可以使用一個名為 groupBy 的很棒的運算子來解決第二個問題!
但是為了利用所有這些優點,你必須有一個 Observable 來源,所以......不是一個方法。
將主題想像成一個開放的(未完成的)空的 Observable。它是表示自訂事件的完美工具。
我們元件中的所有事件都可以用主題來表示:
export class TodoListComponent { ... toggleTodo$ = new Subject<string>(); deleteTodo$ = new Subject<string>(); addTodo$ = new Subject<void>(); }
然後,我們的範本可以在必要時簡單地呼叫它們,而不是呼叫方法,例如:
<button (click)="deleteTodo$.next(todo.id)">delete</button>
現在我們的來源是 Observables,我們可以使用我們親愛的運算符:讓我們創建一些效果。
我喜歡在建構函式中定義我的效果,這樣我就可以在元件被銷毀時使用 takeUntilDestroyed() 運算子來清理效果!因此,例如:
constructor() { this.addTodo$.pipe( concatMap(() => this.todoService.add()) takeUntilDestroyed() ).subscribe(newTodo => this.todos.update(todos => [...todos, newTodo])); }
這裡我使用 concatMap 來保留回應的順序,以便依序新增待辦事項。這意味著沒有並發調用。我認為它對於添加操作是完美的,但對於其他呼叫來說可能是錯誤的選擇:例如,對於 GET 請求,通常最好使用 exhausMap 或 switchMap,具體取決於用例。
我還使用了一種稱為悲觀更新的方法,這意味著我等待通話結束來更新我的內部狀態。這是個人喜好!您可以立即新增待辦事項,然後在 API 呼叫出錯時使用 catchError 將其還原。
然後是 Angular 的實際效果函數,它旨在與訊號結合使用:我使用此函數來執行同步任務。例如,當 URL 中的參數發生變更(指新的實體 ID)時,我可能會想要使用新實體更新表單:
// This comes from the router id = input.required<string>(); // Always stores the current invoice information currentInvoice = toSignal(toObservable(this.id).pipe( switchMap(id => this.invoiceService.get(id)) )); constructor() { effect(() => { // Assuming the 2 structures match, every time we browse // to a new invoice, the form gets populated this.form.patchValue(this.currentInvoice()); }) }
請注意,我們無法使用此技術控制背壓。對於這種事情,這沒問題,但請記住:這就是為什麼我們仍然需要 RxJS 來製作無錯誤的應用程式。或者另一個庫在底層抽象化了這種複雜性。
我們用訊號表示的許多狀態在技術上可以被視為派生的非同步狀態。例如,我們的待辦事項清單可以被視為來自伺服器的派生狀態:
// Trigger this when you need to refetch the todos fetchTodos$ = new Subject<void>(); todos = toSignal(toObservable(this.fetchTodos$).pipe( switchMap(id => this.todoService.getAll()) ));
這種方法類似於 TanStack Query 等庫所使用的方法,在這種方法中,當您需要新資料時,您可以手動使查詢無效。換句話說,每次突變你總是去伺服器。
這在某些情況下可能很好,但有兩件事需要考慮:
總之,我通常不推薦。我說通常! :)
我希望你喜歡這篇短文!總結一下:
我確信,如果您遵循這些原則,您的應用程式將會更容易維護!
以上是我如何使用訊號來建立我的 Angular 元件的詳細內容。更多資訊請關注PHP中文網其他相關文章!