首页 > 其他分享 >什么是 @ngrx/store 开发包中的 MemoizedSelector

什么是 @ngrx/store 开发包中的 MemoizedSelector

时间:2023-07-29 18:33:20浏览次数:49  
标签:状态 ngrx 购物车 state 开发包 Memoized 选择器 store

@ngrx/store 开发包中,MemoizedSelector 是一个重要的概念和工具,用于在 Angular 应用中管理和选择 Redux 状态的片段。它是 @ngrx/store 的一个关键特性,通过使用 reselect 库来实现对状态选择器的优化。MemoizedSelector 可以提高状态选择器的性能,并且能够避免不必要的状态计算,从而提高应用的效率和响应性。

在本文中,我们将深入了解 MemoizedSelector 的概念和用法,并通过具体的示例详细说明它的使用方法和优势。

1. 了解 Memoized Selector

在 Redux 应用中,状态管理是非常重要的。使用 @ngrx/store 开发包可以在 Angular 应用中轻松实现 Redux 模式。其中,选择器(Selector)是用于从 Redux 状态树中获取特定数据片段的函数。选择器允许我们在应用中组织和重用状态的读取逻辑。

@ngrx/store 中的选择器分为两类:

  1. 普通选择器(Selector):每次调用时都会计算和返回新的结果。这可能会导致性能问题,特别是在复杂状态计算时。

  2. Memoized 选择器(MemoizedSelector):使用 reselect 库实现的选择器。Memoized 选择器会缓存计算结果,并在相同的输入条件下直接返回缓存的结果,而不会重新计算。这样可以避免不必要的计算和性能损失。

2. Memoized Selector 的用法

在 Angular 应用中,我们通常会使用 createFeatureSelectorcreateSelector 函数来定义 Memoized 选择器。createFeatureSelector 用于创建特定 feature 下的选择器,而 createSelector 用于创建具体的 Memoized 选择器。

2.1 创建 Feature 选择器(createFeatureSelector)

createFeatureSelector 函数用于创建一个特定的 feature 下的选择器,它接收一个特定 feature 的标识符作为参数,并返回一个选择器函数。该选择器函数会从根状态中选择特定 feature 下的状态片段。

import { createFeatureSelector } from '@ngrx/store';

// 创建 Feature 选择器
const featureSelector = createFeatureSelector<FeatureState>('featureName');

上述代码中,我们创建了一个名为 featureSelector 的 Feature 选择器,并将其与特定 feature 的标识符 'featureName' 关联起来。这样,featureSelector 将用于选择来自名为 'featureName' 的 feature 下的状态片段。

2.2 创建 Memoized 选择器(createSelector)

createSelector 函数用于创建 Memoized 选择器,它接收一系列输入选择器(可以是 Feature 选择器或其他 Memoized 选择器),以及一个输出函数。输出函数用于从输入选择器的结果中选择特定的状态片段,并进行任意的转换或计算。

import { createSelector } from '@ngrx/store';

// 创建 Memoized 选择器
const memoizedSelector = createSelector(
  featureSelector,
  (featureState) => featureState.someData
);

上述代码中,我们创建了一个名为 memoizedSelector 的 Memoized 选择器。它接收来自 featureSelector 的结果 featureState 作为输入,然后从中选择 featureState.someData 这个状态片段。这里的 someData 可以是特定 feature 下的某个属性或者经过复杂计算得到的状态。

Memoized 选择器会缓存 featureState.someData 的计算结果,并在相同的输入条件下直接返回缓存的结果,避免不必要的计算。

2.3 使用 Memoized 选择器

在应用中使用 Memoized 选择器的过程非常简单。我们可以像调用普通函数一样调用 Memoized 选择器,并传入所需的输入条件。Memoized 选择器将根据输入条件进行状态选择,并返回缓存的结果或者进行计算后返回结果。

// 使用 Memoized 选择器
store.select(memoizedSelector).subscribe((data) => {
  console.log('Selected data:', data);
});

上述代码中,我们通过 store.select() 方法调用 memoizedSelector Memoized 选择器,并通过 subscribe 订阅状态的变化。当状态变化时,memoizedSelector 将根据输入条件进行状态选择,并返回缓存的结果或计算后的结果。最终,我们会在控制台中看到所选数据的输出。

3. Memoized Selector 的优势

Memoized 选择器在 Angular 应用中有许多优势,特别是在处理复杂状态计算时:

3.1 提高性能

Memoized 选择器通过缓存计算结果来避免不必要的状态计算,从而提高应用的性能。当选择器的输入条件没有变化时,Memoized 选择器将直接返回之前缓存的结果,而不会重新计算。这在大型应用和复杂状态计算的情况下尤为重要,可以减少重复计算的开销,提升应用的响应性能。

3.2 避免不必要的状态更新

由于 Memoized 选择器会缓存计算结果,当状态变化时,只有依赖于输入条件的数据发生变化时,Memoized 选择器才会返回新的结果。这样可以避免不必要的状态更新,减少不必要的组件重新渲染,提高应用的效率。

3.3 支持复杂状态计算

Memoized 选择器非常适用于处理复杂的状态计算逻辑。通过组合多个选择器和输出函数,我们可以轻松地实现复杂的状态转换和计算。Memoized 选择器的

缓存机制可以确保只有在必要时才进行状态计算,避免重复工作。

3.4 代码重用和组织

Memoized 选择器允许我们将状态选择逻辑从组件中分离出来,实现代码的重用和组织。我们可以将复杂的状态选择逻辑放在 Memoized 选择器中,并在不同的组件中重复使用这些选择器。这样可以保持组件简单,更易于维护。

4. 示例:使用 Memoized 选择器管理购物车状态

现在,我们通过一个购物车示例来演示如何使用 Memoized 选择器来管理复杂的状态逻辑。假设我们有一个购物车应用,其中包含多个商品和购物车状态。每个商品都有一个唯一的 ID、名称、价格和数量。购物车状态是购物车中所有商品的集合。

我们首先定义购物车的状态接口:

interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

interface CartState {
  items: CartItem[];
  total: number;
}

接下来,我们创建购物车的 @ngrx/store 特性模块,并定义 Memoized 选择器来管理购物车状态。

// 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,
};

// 创建购物车状态 reducer
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 }) => {
    // 根据商品 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),
    };
  })
);

在上述代码中,我们首先定义了购物车状态接口 CartState 和商品接口 CartItem。接着,我们创建了两个 @ngrx/store 的 action:addToCartremoveFromCart。这些 action 将用于修改购物车的状态。

然后,我们定义了购物车的状态 reducer cartReducer,其中使用 on 函数来处理不同的 action。在 addToCart 处理逻辑中,我们判断商品是否已经存在于购物车中,如果存在则更新数量和总价,否则将新商品添加到购物车。在 removeFromCart 处理逻辑中,我们根据商品 ID 从购物车中删除商品,并相应地更新总价。

现在,我们来创建 Memoized 选择器来从购物车状态中选择特定数据片段。

// cart.selectors.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { CartState } from './cart-state.interface';

// 创建购物车 Feature 选择器
export const selectCartState = createFeatureSelector<CartState>('cart');

// 创建 Memoized 选择器:选择购物车中的所有商品
export const selectCartItems = createSelector(
  selectCartState,
  (cartState) => cartState.items
);

// 创建 Memoized 选择器:选择购物车中的商品总价
export const selectCartTotal = createSelector(
  selectCartState,
  (cartState) => cartState.total
);

在上述代码中,我们首先使用 createFeatureSelector 创建购物车的 Feature 选择器 selectCartState。接着,我们使用 createSelector 来创建两个 Memoized 选择器:selectCartItemsselectCartTotalselectCartItems Memoized 选择器选择购物车状态中的 items,而 selectCartTotal 选择购物车状态中的 total

现在,我们可以在组件中使用这些 Memoized 选择器来选择购物车状态的特定数据片段。

// 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() 方法调用 selectCartItemsselectCartTotal Memoized 选择器,以获取购物车中的所有商品和总价。然后,我们在模板中使用 async 管道来处理 Observable 数据,实时显示购物车的商品列表和总价。

通过使用 Memoized 选择器,我们可以高效地管理购物车状态,并根据购物车中的商品动态更新应用界面。

5. 总结

MemoizedSelector@ngrx/store 开发包中的一个重要概念,它通过使用 reselect 库来实现对状态选择器的优化。Memoized 选择器可以提高应用的性能,避免不必要的状态计算,并支持复杂状态逻辑的处理。

通过 createFeatureSelectorcreateSelector 函数,我们可以轻松地创建特定 feature 下的选择器和 Memoized 选择器。Memoized 选择器在 Angular 应用中非常有用,特别是在管理大型状态树和复杂状态计算时,能够有效地提高应用的性能和响应性。

在实际开发中,我们应该充分利用 MemoizedSelector 的优势,并将状态选择逻辑尽可能地抽象成可复用的选择器,以提高代码的可维护性和重用性。同时,我们也应该注意避免滥用 Memoized 选择器,避免创建过多的选择器导致不必要的内存占用。综合考虑性能和可维护性,合理使用 Memoized 选择器将有助于构建高效、可扩展的 Angular 应用。

标签:状态,ngrx,购物车,state,开发包,Memoized,选择器,store
From: https://www.cnblogs.com/sap-jerry/p/17590264.html

相关文章

  • iptables-restore
    iptables-restore还原iptables表的配置补充说明iptables-restore命令用来还原iptables-save命令所备份的iptables配置。语法iptables-restore(选项)选项-c:指定在还原iptables表时候,还原当前的数据包计数器和字节计数器的值;-t:指定要还原表的名称。实例iptables-restor......
  • How to Restore ASM Based OCR After Complete Loss of the CRS Diskgroup on Linux/U
    InthisDocumentGoalSolutionReferencesAPPLIESTO:OracleDatabase-EnterpriseEdition-Version11.2.0.1.0andlaterOracleDatabaseCloudSchemaService-VersionN/AandlaterGen1ExadataCloudatCustomer(OracleExadataDatabaseCloudMachine)......
  • angular/platform-browser 开发包里的 BrowserModule
    在Angular中,BrowserModule是一个关键的模块,它提供了在浏览器中运行应用程序所必需的服务和指令。这个模块是由@angular/platform-browser包提供的。BrowserModule为我们提供了很多基础的服务、组件和指令,如CommonModule、ApplicationModule、PlatformModule、APP_ID_RANDO......
  • 我的章管家AppStore技术支持
    我的章管家,一键生成电子印章【产品功能】输入名称和编号,可一键生成印章:可以查看我的印章列表,预览印章效果,复制印章文本信息,可以创建印章,删除印章,保存印章到手机相册。如果您在使用过程中有问题请留言或发邮件给我谢谢!Ifyouhaveanyquestions,pleaseleaveamessageorsendm......
  • android.provider.MediaStore.Video.Media
    实现android.provider.MediaStore.Video.Media的步骤对于刚入行的小白开发者来说,实现android.provider.MediaStore.Video.Media可能会感到困惑。不用担心,本文将指导你完成这个任务。下面是实现该功能的步骤及相应的代码解释。步骤步骤描述1.导入所需的库2.创建查......
  • android生成jks和keystore
    Android生成JKS和Keystore在Android开发中,我们经常需要为应用程序生成数字证书,以确保应用程序的安全性和完整性。生成JKS(JavaKeyStore)和Keystore是Android开发中的一项重要任务。本文将介绍什么是JKS和Keystore,以及如何使用AndroidStudio生成它们。我们还将提供示例代码来演示如......
  • 2023年iOS App Store上架流程详解(上)
    ​ 很多开发者在开发完iOSAPP、进行内测后,下一步就面临上架AppStore,不过也有很多同学对APP上架AppStore的流程不太了解,下面我们来说一下iOSAPP上架AppStore的具体流程,如有未涉及到的部分,大家可以及时咨询,共同探讨。内容:在完成iOSAPP开发和内部测试后,下一个步骤就是将应用......
  • iOS App Store上架流程详解
    ​ 很多开发者在开发完iOSAPP、进行内测后,下一步就面临上架AppStore,不过也有很多同学对APP上架AppStore的流程不太了解,下面我们来说一下iOSAPP上架AppStore的具体流程,如有未涉及到的部分,大家可以及时咨询,共同探讨。内容:在完成iOSAPP开发和内部测试后,下一个步骤就是将应用......
  • 苹果iOS App Store上架操作流程详解:从开发者账号到应用发布
    ​ 很多开发者在开发完iOSAPP、进行内测后,下一步就面临上架AppStore,不过也有很多同学对APP上架AppStore的流程不太了解,下面我们来说一下iOSAPP上架AppStore的具体流程,如有未涉及到的部分,大家可以及时咨询,共同探讨。内容:在完成iOSAPP开发和内部测试后,下一个步骤就是将应......
  • 2023年iOS App Store上架流程详解(上)
    ​ 在2023年,随着苹果发布机制的微调,有些关于iOSApp上架流程的资料已经过时。本文将根据最新的要求和经验,详细介绍iOSApp上架的流程。1.注册开发者账号首先,您需要注册一个AppleDeveloper的开发者账号。这个账号的年费大约是600多元人民币。注册过程可以在AppleDeveloper......