NgRx Selector 的 Memoization 特性学习笔记

2021年09月15日 阅读数:8
这篇文章主要向大家介绍NgRx Selector 的 Memoization 特性学习笔记,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

在计算机编程领域中,memoization 或 memoisation 是一种优化技术,主要用于经过存储昂贵的函数调用的结果并在再次出现相同的输入时返回缓存的结果来加速计算机程序。 算法

Memoization 也已用于其余上下文(以及速度增益之外的目的),例如在简单的相互递归降低解析中。尽管与缓存有关,但记忆化是指此优化的特定状况,将其与缓存或页面替换等缓存形式区分开来。在某些逻辑编程语言的上下文中,记忆化也称为 Tabling.typescript

记忆功能“记住”与某些特定输入集相对应的结果。使用记住输入的后续调用返回记住的结果而不是从新计算它,从而消除了使用给定参数调用的主要成本,除了第一次使用这些参数调用函数。记住的关联集能够是由替换算法控制的固定大小的集,也能够是固定集,这取决于函数的性质及其用途。一个函数只有在引用透明的状况下才能被记忆;也就是说,仅当调用该函数与用其返回值替换该函数调用具备彻底相同的效果时。 (然而,存在此限制的特殊状况例外。)虽然与查找表相关,但因为记忆在其实现中常常使用此类表,所以记忆会根据须要即时而不是提早填充其结果缓存。编程

记忆化是一种以牺牲空间成本的方法来下降函数时间成本的优化方式;也就是说,记忆化的函数会针对速度进行优化,付出的代价是对计算机内存空间的更高使用率。算法的时间/空间“成本”在计算中有一个特定的名称:计算复杂度。全部函数在时间(即它们须要时间来执行)和空间上都具备计算复杂性。数组

什么是 NgRx selector

选择器是用于获取存储状态切片( store state slices)的纯函数。@ngrx/store 提供了一些帮助函数来优化这个选择。 缓存

Selector 也符合单一职责及 Single responsibility,一个 selector 只 touch State 的一个片断。框架

使用 createSelector 和 createFeatureSelector 函数时,@ngrx/store 会跟踪调用选择器函数的最新参数。 由于选择器是纯函数,当参数匹配时能够返回最后一个结果,而无需从新调用选择器函数。这能够提供性能优点,特别是对于执行昂贵计算的选择器。这种作法被称为记忆。编程语言

换言之,咱们虽然实现了 Selector,可是运行时,这些 selector 可能只会被执行一次,由于若是输入参数相同,NgRx 框架会使用缓存的结果直接返回给调用者,而不会重复调用咱们的 Selector.函数

例子:Using a selector for one piece of state性能

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

export interface FeatureState {
  counter: number;
}

export interface AppState {
  feature: FeatureState;
}

export const selectFeature = (state: AppState) => state.feature;

export const selectFeatureCount = createSelector(
  selectFeature,
  (state: FeatureState) => state.counter
);

console.log('ok');

例子:Using selectors for multiple pieces of state

createSelector 可用于基于相同状态(State)的多个切片从状态中选择一些数据。大数据

createSelector 函数最多可使用 8 个选择器函数,用于更完整的状态选择。

例如,假设您在状态中有一个 selectedUser 对象。 您还有一个 allBooks 书籍对象数组。

而且您想显示当前用户的全部书籍。

您可使用 createSelector 来实现这一点。即便您在 allBooks 中更新它们,您的可见书籍也将始终是最新的。 若是选择了一个,它们将始终显示属于您的用户的书籍,若是没有选择用户,它们将显示全部书籍。

CreateSelector 里传入纯函数。

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

export interface User {
  id: number;
  name: string;
}

export interface Book {
  id: number;
  userId: number;
  name: string;
}

export interface AppState {
  selectedUser: User;
  allBooks: Book[];
}

export const selectUser = (state: AppState) => state.selectedUser;
export const selectAllBooks = (state: AppState) => state.allBooks;

export const selectVisibleBooks = createSelector(
  selectUser,
  selectAllBooks,
  (selectedUser: User, allBooks: Book[]) => {
    if (selectedUser && allBooks) {
      return allBooks.filter((book: Book) => book.userId === selectedUser.id);
    } else {
      return allBooks;
    }
  }
);

Selecting Feature States

createFeatureSelector 是返回顶级功能状态(Top Level Feature State)的便捷方法。它为状态的特征切片返回一个类型化的选择器函数。

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

export const featureKey = 'feature';

export interface FeatureState {
  counter: number;
}

export interface AppState {
  feature: FeatureState;
}

export const selectFeature = createFeatureSelector<AppState, FeatureState>(featureKey);

export const selectFeatureCount = createSelector(
  selectFeature,
  (state: FeatureState) => state.counter
);

Resetting Memoized Selectors

经过调用 createSelector 或 createFeatureSelector 返回的选择器函数最初的记忆值为 null。 在第一次调用选择器后,其记忆值存储在内存中。 若是随后使用相同的参数调用选择器,它将返回记忆值。 若是随后使用不一样的参数调用选择器,它将从新计算并更新其记忆值。

一个例子:

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

export interface State {
  counter1: number;
  counter2: number;
}

export const selectCounter1 = (state: State) => state.counter1;
export const selectCounter2 = (state: State) => state.counter2;
export const selectTotal = createSelector(
  selectCounter1,
  selectCounter2,
  (counter1, counter2) => counter1 + counter2
); // selectTotal has a memoized value of null, because it has not yet been invoked.

let state = { counter1: 3, counter2: 4 };

selectTotal(state); // computes the sum of 3 & 4, returning 7. selectTotal now has a memoized value of 7
selectTotal(state); // does not compute the sum of 3 & 4. selectTotal instead returns the memoized value of 7

state = { ...state, counter2: 5 };

selectTotal(state); // computes the sum of 3 & 5, returning 8. selectTotal now has a memoized value of 8

选择器的记忆值无限期地保留在内存中。 例如,若是记忆值是再也不须要的大数据集,则能够将记忆值重置为 null,以即可以从内存中删除大数据集。 这能够经过调用选择器上的 release 方法来完成。

selectTotal(state); // returns the memoized value of 8
selectTotal.release(); // memoized value of selectTotal is now null