詳解什么是@ngrx/store開發(fā)包中的MemoizedSelector
1. 了解 Memoized Selector
在 @ngrx/store
開發(fā)包中,MemoizedSelector
是一個重要的概念和工具,用于在 Angular 應(yīng)用中管理和選擇 Redux 狀態(tài)的片段。它是 @ngrx/store
的一個關(guān)鍵特性,通過使用 reselect
庫來實(shí)現(xiàn)對狀態(tài)選擇器的優(yōu)化。MemoizedSelector
可以提高狀態(tài)選擇器的性能,并且能夠避免不必要的狀態(tài)計算,從而提高應(yīng)用的效率和響應(yīng)性。
在本文中,我們將深入了解 MemoizedSelector
的概念和用法,并通過具體的示例詳細(xì)說明它的使用方法和優(yōu)勢。
在 Redux 應(yīng)用中,狀態(tài)管理是非常重要的。使用 @ngrx/store
開發(fā)包可以在 Angular 應(yīng)用中輕松實(shí)現(xiàn) Redux 模式。其中,選擇器(Selector)是用于從 Redux 狀態(tài)樹中獲取特定數(shù)據(jù)片段的函數(shù)。選擇器允許我們在應(yīng)用中組織和重用狀態(tài)的讀取邏輯。
@ngrx/store
中的選擇器分為兩類:
- 普通選擇器(Selector):每次調(diào)用時都會計算和返回新的結(jié)果。這可能會導(dǎo)致性能問題,特別是在復(fù)雜狀態(tài)計算時。
- Memoized 選擇器(MemoizedSelector):使用
reselect
庫實(shí)現(xiàn)的選擇器。Memoized 選擇器會緩存計算結(jié)果,并在相同的輸入條件下直接返回緩存的結(jié)果,而不會重新計算。這樣可以避免不必要的計算和性能損失。
2. Memoized Selector 的用法
在 Angular 應(yīng)用中,我們通常會使用 createFeatureSelector
和 createSelector
函數(shù)來定義 Memoized 選擇器。createFeatureSelector
用于創(chuàng)建特定 feature 下的選擇器,而 createSelector
用于創(chuàng)建具體的 Memoized 選擇器。
2.1 創(chuàng)建 Feature 選擇器(createFeatureSelector)
createFeatureSelector
函數(shù)用于創(chuàng)建一個特定的 feature 下的選擇器,它接收一個特定 feature 的標(biāo)識符作為參數(shù),并返回一個選擇器函數(shù)。該選擇器函數(shù)會從根狀態(tài)中選擇特定 feature 下的狀態(tài)片段。
import { createFeatureSelector } from '@ngrx/store'; // 創(chuàng)建 Feature 選擇器 const featureSelector = createFeatureSelector<FeatureState>('featureName');
上述代碼中,我們創(chuàng)建了一個名為 featureSelector
的 Feature 選擇器,并將其與特定 feature 的標(biāo)識符 'featureName'
關(guān)聯(lián)起來。這樣,featureSelector
將用于選擇來自名為 'featureName'
的 feature 下的狀態(tài)片段。
2.2 創(chuàng)建 Memoized 選擇器(createSelector)
createSelector
函數(shù)用于創(chuàng)建 Memoized 選擇器,它接收一系列輸入選擇器(可以是 Feature 選擇器或其他 Memoized 選擇器),以及一個輸出函數(shù)。輸出函數(shù)用于從輸入選擇器的結(jié)果中選擇特定的狀態(tài)片段,并進(jìn)行任意的轉(zhuǎn)換或計算。
import { createSelector } from '@ngrx/store'; // 創(chuàng)建 Memoized 選擇器 const memoizedSelector = createSelector( featureSelector, (featureState) => featureState.someData );
上述代碼中,我們創(chuàng)建了一個名為 memoizedSelector
的 Memoized 選擇器。它接收來自 featureSelector
的結(jié)果 featureState
作為輸入,然后從中選擇 featureState.someData
這個狀態(tài)片段。這里的 someData
可以是特定 feature 下的某個屬性或者經(jīng)過復(fù)雜計算得到的狀態(tài)。
Memoized 選擇器會緩存 featureState.someData
的計算結(jié)果,并在相同的輸入條件下直接返回緩存的結(jié)果,避免不必要的計算。
2.3 使用 Memoized 選擇器
在應(yīng)用中使用 Memoized 選擇器的過程非常簡單。我們可以像調(diào)用普通函數(shù)一樣調(diào)用 Memoized 選擇器,并傳入所需的輸入條件。Memoized 選擇器將根據(jù)輸入條件進(jìn)行狀態(tài)選擇,并返回緩存的結(jié)果或者進(jìn)行計算后返回結(jié)果。
// 使用 Memoized 選擇器 store.select(memoizedSelector).subscribe((data) => { console.log('Selected data:', data); });
上述代碼中,我們通過 store.select()
方法調(diào)用 memoizedSelector
Memoized 選擇器,并通過 subscribe
訂閱狀態(tài)的變化。當(dāng)狀態(tài)變化時,memoizedSelector
將根據(jù)輸入條件進(jìn)行狀態(tài)選擇,并返回緩存的結(jié)果或計算后的結(jié)果。最終,我們會在控制臺中看到所選數(shù)據(jù)的輸出。
3. Memoized Selector 的優(yōu)勢
Memoized 選擇器在 Angular 應(yīng)用中有許多優(yōu)勢,特別是在處理復(fù)雜狀態(tài)計算時:
3.1 提高性能
Memoized 選擇器通過緩存計算結(jié)果來避免不必要的狀態(tài)計算,從而提高應(yīng)用的性能。當(dāng)選擇器的輸入條件沒有變化時,Memoized 選擇器將直接返回之前緩存的結(jié)果,而不會重新計算。這在大型應(yīng)用和復(fù)雜狀態(tài)計算的情況下尤為重要,可以減少重復(fù)計算的開銷,提升應(yīng)用的響應(yīng)性能。
3.2 避免不必要的狀態(tài)更新
由于 Memoized 選擇器會緩存計算結(jié)果,當(dāng)狀態(tài)變化時,只有依賴于輸入條件的數(shù)據(jù)發(fā)生變化時,Memoized 選擇器才會返回新的結(jié)果。這樣可以避免不必要的狀態(tài)更新,減少不必要的組件重新渲染,提高應(yīng)用的效率。
3.3 支持復(fù)雜狀態(tài)計算
Memoized 選擇器非常適用于處理復(fù)雜的狀態(tài)計算邏輯。通過組合多個選擇器和輸出函數(shù),我們可以輕松地實(shí)現(xiàn)復(fù)雜的狀態(tài)轉(zhuǎn)換和計算。Memoized 選擇器的
緩存機(jī)制可以確保只有在必要時才進(jìn)行狀態(tài)計算,避免重復(fù)工作。
3.4 代碼重用和組織
Memoized 選擇器允許我們將狀態(tài)選擇邏輯從組件中分離出來,實(shí)現(xiàn)代碼的重用和組織。我們可以將復(fù)雜的狀態(tài)選擇邏輯放在 Memoized 選擇器中,并在不同的組件中重復(fù)使用這些選擇器。這樣可以保持組件簡單,更易于維護(hù)。
4. 示例:使用 Memoized 選擇器管理購物車狀態(tài)
現(xiàn)在,我們通過一個購物車示例來演示如何使用 Memoized 選擇器來管理復(fù)雜的狀態(tài)邏輯。假設(shè)我們有一個購物車應(yīng)用,其中包含多個商品和購物車狀態(tài)。每個商品都有一個唯一的 ID、名稱、價格和數(shù)量。購物車狀態(tài)是購物車中所有商品的集合。
我們首先定義購物車的狀態(tài)接口:
interface CartItem { id: number; name: string; price: number; quantity: number; } interface CartState { items: CartItem[]; total: number; }
接下來,我們創(chuàng)建購物車的 @ngrx/store
特性模塊,并定義 Memoized 選擇器來管理購物車狀態(tài)。
// cart.actions.ts import { createAction, props } from '@ngrx/store'; import { CartItem } from './cart-state.interface'; // 添加商品到購物車 export const addToCart = createAction( '[Cart] Add To Cart', props<{ item: CartItem }>() ); // 從購物車中刪除商品 export const removeFromCart = createAction( '[Cart] Remove From Cart', props<{ itemId: number }>() ); // cart.reducer.ts import { createReducer, on } from '@ngrx/store'; import { CartState, CartItem } from './cart-state.interface'; import { addToCart, removeFromCart } from './cart.actions'; // 初始化購物車狀態(tài) const initialState: CartState = { items: [], total: 0, }; // 創(chuàng)建購物車狀態(tài) reducer export const cartReducer = createReducer( initialState, on(addToCart, (state, { item }) => { // 判斷商品是否已存在于購物車中 const existingItem = state.items.find((i) => i.id === item.id); if (existingItem) { // 商品已存在,更新數(shù)量和總價 const updatedItems = state.items.map((i) => i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i ); return { ...state, items: updatedItems, total: state.total + item.price, }; } else { // 商品不存在,添加新商品到購物車 const newItem: CartItem = { ...item, quantity: 1 }; return { ...state, items: [...state.items, newItem], total: state.total + item.price, }; } }), on(removeFromCart, (state, { itemId }) => { // 根據(jù)商品 ID 從購物車中刪除商品 const updatedItems = state.items.filter((i) => i.id !== itemId); const removedItem = state.items.find((i) => i.id === itemId); return { ...state, items: updatedItems, total: state.total - (removedItem ? removedItem.price * removedItem.quantity : 0), }; }) );
在上述代碼中,我們首先定義了購物車狀態(tài)接口 CartState
和商品接口 CartItem
。接著,我們創(chuàng)建了兩個 @ngrx/store
的 action:addToCart
和 removeFromCart
。這些 action 將用于修改購物車的狀態(tài)。
然后,我們定義了購物車的狀態(tài) reducer cartReducer
,其中使用 on
函數(shù)來處理不同的 action。在 addToCart
處理邏輯中,我們判斷商品是否已經(jīng)存在于購物車中,如果存在則更新數(shù)量和總價,否則將新商品添加到購物車。在 removeFromCart
處理邏輯中,我們根據(jù)商品 ID 從購物車中刪除商品,并相應(yīng)地更新總價。
現(xiàn)在,我們來創(chuàng)建 Memoized 選擇器來從購物車狀態(tài)中選擇特定數(shù)據(jù)片段。
// cart.selectors.ts import { createFeatureSelector, createSelector } from '@ngrx/store'; import { CartState } from './cart-state.interface'; // 創(chuàng)建購物車 Feature 選擇器 export const selectCartState = createFeatureSelector<CartState>('cart'); // 創(chuàng)建 Memoized 選擇器:選擇購物車中的所有商品 export const selectCartItems = createSelector( selectCartState, (cartState) => cartState.items ); // 創(chuàng)建 Memoized 選擇器:選擇購物車中的商品總價 export const selectCartTotal = createSelector( selectCartState, (cartState) => cartState.total );
在上述代碼中,我們首先使用 createFeatureSelector
創(chuàng)建購物車的 Feature 選擇器 selectCartState
。接著,我們使用 createSelector
來創(chuàng)建兩個 Memoized 選擇器:selectCartItems
和 selectCartTotal
。selectCartItems
Memoized 選擇器選擇購物車狀態(tài)中的 items
,而 selectCartTotal
選擇購物車狀態(tài)中的 total
。
現(xiàn)在,我們可以在組件中使用這些 Memoized 選擇器來選擇購物車狀態(tài)的特定數(shù)據(jù)片段。
// cart.component.ts import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; import { CartItem } from './cart-state.interface'; import { addToCart, removeFromCart } from './cart.actions'; import { selectCartItems, selectCartTotal } from './cart.selectors'; import { Observable } from 'rxjs'; @Component({ selector: 'app-cart', template: ` <h2>Shopping Cart</h2> <div *ngFor="let item of cartItems$ | async"> <p>{{ item.name }} - Quantity: {{ item.quantity }} - Price: ${{ item.price }}</p> <button (click)="removeItemFromCart(item.id)">Remove</button> </div> <p>Total Price: ${{ cartTotal$ | async }}</p> `, }) export class CartComponent { cartItems$: Observable<CartItem[]>; cartTotal$: Observable<number>; constructor(private store: Store) { this.cartItems$ = this.store.select(selectCartItems); this.cartTotal$ = this.store.select(selectCartTotal); } addItemToCart(item: CartItem) { this.store.dispatch(addToCart({ item })); } removeItemFromCart(itemId: number) { this.store.dispatch(removeFromCart({ itemId })); } }
在購物車組件中,我們使用 store.select()
方法調(diào)用 selectCartItems
和 selectCartTotal
Memoized 選擇器,以獲取購物車中的所有商品和總價。然后,我們在模板中使用 async
管道來處理 Observable 數(shù)據(jù),實(shí)時顯示購物車的商品列表和總價。
通過使用 Memoized 選擇器,我們可以高效地管理購物車狀態(tài),并根據(jù)購物車中的商品動態(tài)更新應(yīng)用界面。
5. 總結(jié)
MemoizedSelector
是 @ngrx/store
開發(fā)包中的一個重要概念,它通過使用 reselect
庫來實(shí)現(xiàn)對狀態(tài)選擇器的優(yōu)化。Memoized 選擇器可以提高應(yīng)用的性能,避免不必要的狀態(tài)計算,并支持復(fù)雜狀態(tài)邏輯的處理。
通過 createFeatureSelector
和 createSelector
函數(shù),我們可以輕松地創(chuàng)建特定 feature 下的選擇器和 Memoized 選擇器。Memoized 選擇器在 Angular 應(yīng)用中非常有用,特別是在管理大型狀態(tài)樹和復(fù)雜狀態(tài)計算時,能夠有效地提高應(yīng)用的性能和響應(yīng)性。
在實(shí)際開發(fā)中,我們應(yīng)該充分利用 MemoizedSelector
的優(yōu)勢,并將狀態(tài)選擇邏輯盡可能地抽象成可復(fù)用的選擇器,以提高代碼的可維護(hù)性和重用性。同時,我們也應(yīng)該注意避免濫用 Memoized 選擇器,避免創(chuàng)建過多的選擇器導(dǎo)致不必要的內(nèi)存占用。綜合考慮性能和可維護(hù)性,合理使用 Memoized 選擇器將有助于構(gòu)建高效、可擴(kuò)展的 Angular 應(yīng)用。
以上就是什么是@ngrx/store開發(fā)包中的MemoizedSelector的詳細(xì)內(nèi)容,更多關(guān)于@ngrx/store開發(fā)包MemoizedSelector的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
AngularJS實(shí)現(xiàn)圖片上傳和預(yù)覽功能的方法分析
這篇文章主要介紹了AngularJS實(shí)現(xiàn)圖片上傳和預(yù)覽功能的方法,結(jié)合HTML5實(shí)例形式對比分析了AngularJS圖片上傳的相關(guān)操作技巧與注意事項,需要的朋友可以參考下2017-11-11AngularJS報錯$apply already in progress的解決方法分析
這篇文章主要介紹了AngularJS報錯$apply already in progress的解決方法,較為詳細(xì)的分析了報錯$apply already in progress的原理、處理技巧與相關(guān)注意事項,需要的朋友可以參考下2017-01-01Angular.js實(shí)現(xiàn)注冊系統(tǒng)的實(shí)例詳解
Angular.js是Google開發(fā)的前端技術(shù)框架,最近一直在學(xué)習(xí)Angular.js,通過對angular.js的簡單理解后發(fā)現(xiàn),angular.js通過一些簡單的指令即可實(shí)現(xiàn)對DOM元素的操作,其特色為雙向數(shù)據(jù)綁定,下面這篇文章給大家詳細(xì)介紹Angular.js實(shí)現(xiàn)注冊系統(tǒng)的方法,一起來看看吧。2016-12-12