1. 了解 Memoized Selector 在 @ngrx/store 生态中,MemoizedSelector 是一个核心概念,也是优化性能的重要手段。简单来说,它借助 reselect 库,让状态选择器具备“记忆”能力——缓存计算结果,避免重复计算。这样,应用的响应速度和效率都能得到显著提升
@ngrx/store中的MemoizedSelector详解
时间:2026-06-11
浏览:452
文章目录
1. 了解 Memoized Selector
在 @ngrx/store 生态中,MemoizedSelector 是一个核心概念,也是优化性能的重要手段。简单来说,它借助 reselect 库,让状态选择器具备“记忆”能力——缓存计算结果,避免重复计算。这样,应用的响应速度和效率都能得到显著提升。

接下来这篇文章将详细拆解 MemoizedSelector 的原理、用法,以及它在实际项目中能带来的实际好处。最后还会通过一个购物车的完整示例,帮助你把理论落实到代码中。
在 Redux 应用中,状态管理的重要性不言而喻。@ngrx/store 使 Angular 项目能够轻松接入 Redux 模式。而选择器(Selector)就是关键工具——从状态树中精确取出所需的数据。选择器可以集中组织状态的读取逻辑,提高复用性。
在 @ngrx/store 中,选择器分为两种:
- 普通选择器:每次调用都会重新计算,返回新结果。如果状态逻辑复杂,频繁调用容易造成性能瓶颈。
- Memoized 选择器:基于
reselect实现,内部有缓存机制。只要输入条件不变,直接返回缓存结果,不会重新计算。这是性能优化的关键。
2. Memoized Selector 的用法
在 Angular 项目中,主要通过 createFeatureSelector 和 createSelector 这两个函数来定义 Memoized 选择器。前者用于锁定某个 feature 模块的状态,后者则创建具体的记忆化选择器。
2.1 创建 Feature 选择器(createFeatureSelector)
createFeatureSelector 接收一个特征标识符(字符串),返回一个选择器函数,该函数能从根状态中提取对应 feature 下的整个状态片段。
import { createFeatureSelector } from '@ngrx/store';
const featureSelector = createFeatureSelector('featureName');
这段代码创建了一个名为 featureSelector 的 Feature 选择器,绑定到 'featureName' 标识符。之后用它就能直接拿到 featureName 模块下的全部状态。
2.2 创建 Memoized 选择器(createSelector)
createSelector 是真正的核心。它接收若干个输入选择器(可以是 Feature 选择器或其他 Memoized 选择器),然后加上一个输出函数,用来从输入结果中提取或计算最终所需的数据。
import { createSelector } from '@ngrx/store';
const memoizedSelector = createSelector(
featureSelector,
(featureState) => featureState.someData
);
这里的 memoizedSelector 会先从 featureSelector 拿到 featureState,然后只取出 .someData 部分。如果输入条件(即 featureState 对象引用)没有改变,它会直接返回上次缓存的结果,不再执行读取操作。
2.3 使用 Memoized 选择器
实际使用非常自然——像调用普通函数一样调用选择器,然后交给 store 的 select 方法即可。
store.select(memoizedSelector).subscribe((data) => {
console.log('Selected data:', data);
});
当状态变化时,memoizedSelector 会自动判断是否需要重新计算;只有依赖的输入发生变化,才会产生新结果。这保证了组件不会因为无关的状态变更而频繁重渲染。
3. Memoized Selector 的优势
了解了用法之后,再看它到底厉害在哪里。
3.1 提高性能
记忆化缓存直接省去了大量重复计算。尤其是在大型应用或复杂的状态推导场景中,效果立竿见影。输入条件不变,结果直接取缓存,响应速度自然提升。
3.2 避免不必要的状态更新
由于缓存机制,只有当真正依赖的数据发生变化时,选择器才会返回新值。这能有效减少组件和管道的重新渲染,整个应用因此更流畅。
3.3 支持复杂状态计算
通过组合多个选择器和输出函数,可以轻松实现多层嵌套的状态转换。而缓存机制能确保这种复杂计算只在必要时执行,不会白白浪费性能。
3.4 代码重用和组织
将状态选择逻辑从组件中剥离出来,集中到选择器文件里,然后在多个组件间复用。组件变得更轻量,维护成本也直线下降。
4. 示例:使用 Memoized 选择器管理购物车状态
理论讲再多,不如一个实际例子来得实在。下面用购物车场景来演示 Memoized 选择器如何管理复杂状态。
假设购物车包含多个商品,每个商品有 ID、名称、价格和数量。购物车状态则是所有商品的集合。
先定义接口:
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
interface CartState {
items: CartItem[];
total: number;
}
接着创建 NgRx 特性模块,定义 action 和 reducer。
// 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';
const initialState: CartState = {
items: [],
total: 0,
};
export const cartReducer = createReducer(
initialState,
on(addToCart, (state, { item }) => {
const existingItem = state.items.find((i) => i.id === item.id);
if (existingItem) {
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 }) => {
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),
};
})
);
然后是最关键的部分——创建 Memoized 选择器:
// cart.selectors.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { CartState } from './cart-state.interface';
export const selectCartState = createFeatureSelector('cart');
export const selectCartItems = createSelector(
selectCartState,
(cartState) => cartState.items
);
export const selectCartTotal = createSelector(
selectCartState,
(cartState) => cartState.total
);
在组件里使用这些选择器,就像呼吸一样自然:
// 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: `
Shopping Cart
{{ item.name }} - Quantity: {{ item.quantity }} - Price: ${{ item.price }}
Total Price: ${{ cartTotal$ | async }}
`,
})
export class CartComponent {
cartItems$: Observable;
cartTotal$: Observable;
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 订阅了 selectCartItems 和 selectCartTotal 这两个 Memoized 选择器,模板用 async 管道实时展示数据。当用户添加或删除商品时,只有真正依赖的状态片段发生变化,选择器才会推送新值,组件也随之更新。整个过程高效、清晰。
5. 总结
MemoizedSelector 是 @ngrx/store 中一个非常实用的特性,它通过记忆化机制极大优化了状态选择器的性能。借助 createFeatureSelector 和 createSelector,可以轻松组合出复杂但高效的状态提取逻辑。
在实际项目中,把状态选择逻辑抽象成可复用的 Memoized 选择器,既能让组件保持简洁,又提升了整体性能。当然,也要避免滥用——如果创建太多细粒度的选择器,反而会增加内存开销。平衡好性能与维护性,才是最佳实践。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。