NgRx Selector 的 Memoization 特性学习笔记

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

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

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

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

什么是 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
(0)

相关推荐

  • 前端进阶-手写Vue2.0源码(三)|技术点评

    前言 今天是个特别的日子 祝各位女神女神节快乐哈 封面我就放一张杀殿的帅照表达我的祝福 哈哈 此篇主要手写 Vue2.0 源码-初始渲染原理 上一篇咱们主要介绍了 Vue 模板编译原理 它是 Vue ...

  • Svelte 码半功倍

    你未注意到的最重要的指标. 注意:原文发表于2019-04-20,随着框架不断演进,部分内容可能已不适用. 所有代码都有 BUG,你写的越多,BUG 越多,这很合情合理. 同时,写的越多,费时越多,留 ...

  • 前端开发技术之Vue3 相关基础知识点的整理分享

    多个根节点编辑器不会报错 Vue3是允许我们有多个根节点的,但是我们如果使用Vetur就会报错,不会影响运行,但是看起来就很烦.所以当我们转向Volar那么就不会出现这个问题了. image.png ...

  • 声明式渲染与 data 函数

    目录 声明式渲染 data 必须是一个函数 为什么 data 必须是一个函数? 提一个 data 作为箭头函数的写法 源码 声明式渲染 这是学习vue开发,必先了解的第一个特征.如前已经实现的App. ...

  • 熬夜7天,我总结了JavaScript与ES的25个知识点

    前言 说起JavaScript,大家都知道是一门脚本语言.那么ES是什么鬼呢?ES全称ECMAScript ,是JavaScript语言的国际标准. 最近,我总结了25条JavaScript的基础特性 ...

  • 269个JavaScript工具函数,助你提升工作效率(下)

    原创 叫我詹躲躲 前端技术优选 作者:叫我詹躲躲 原文地址:https://juejin.im/post/5edb6c6be51d4578a2555a9b 130.blob转file /** * @p ...

  • 如何手写Vue-next响应式呢?本文详解

    前言 1.本文将从零开始手写一份vue-next中的响应式原理,出于篇幅和理解的难易程度,我们将只实现核心的api并忽略一些边界的功能点 本文将实现的api包括 track trigger effec ...

  • 手写一个webpack,看看AST怎么用

    Vue中文社区 以下文章来源于进击的大前端 ,作者蒋鹏飞 Dennis 进击的大前端前端工程师,2020年开始写博客.一年时间成为掘金"优秀作者",思否2020年度"To ...

  • Vue3.x 从零开始(六)—— Router + Vuex + TypeScript 实战演练(下)

    在上一篇实战演练中,已经将项目搭建好,并介绍了 Router.Vuex 的基本用法 接下来会以一个 Todo List 项目来介绍实战中如何使用 Composition API 一.输入框与列表(按键 ...

  • HCIE考试中“安全特性”学习笔记(一)

    安全特性 一.DHCP Snooping [Huawei]dhcp snooping enable   开启DHCP-snooping功能 [Huawei-Vlanif1]vlan 1 [Huawei ...

  • 华为认证HCIE“安全特性”学习笔记(二)

    AAA 1.接入问题 (1)传统接入问题: ① 传统接入问题: 在用户接入网络的时候,运营商就不得不面临以下的问题.如何确定用户是否有权限进入网络.运营商需要防止非法用户访问网络,以保护自身和合法用户 ...

  • 【学习笔记】考虑电源特性的逆变器控制参数选择

    摘  要: 可再生能源渗透率较高的孤立微电网中,储能系统作为备用电源,其控制对系统电压和频率的稳定性有着重要的影响.建立了包括储能模型在内的微电网数学模型,综合考虑储能状态及逆变器控制参数对系统的影响 ...

  • 【学习笔记】传感器技术(9):管式光纤光栅温度传感器封装与传感特性研究

    摘  要: 介绍了两种管式光纤光栅温度传感器的金属型封装方案,对其温度传感特性进行了实验研究与分析.使用外径5 mm.内径4 mm.长度50 mm的管式结构不锈钢材料对光纤光栅进行探头式保护型封装以及 ...

  • 一则公报案例学习笔记:对修改股东出资期限应否适用资本多数决规则的思考|审判研究

    一.问题的提出 2021年第3期<最高人民法院公报案例>刊登了鸿大(上海)投资管理有限公司与姚锦城公司决议纠纷上诉案,裁判要旨为:"公司股东滥用控股地位,以多数决方式通过修改出资 ...

  • JAVA多线程学习笔记整理

    多线程: 三种创建方法 继承Thread类,以线程运行内容重写run方法,创建Thread对象并用start方法启动该线程. (匿名内部类) (Lambda表达式) 实现Runable接口,以线程运行 ...

  • 周哥学习笔记(2021.5.8)

    心理界限存在的意义,正是为了帮助人们控制情绪进入的量,不至于太过冷漠或太过投入,让我们保持一个合适的距离与外界互动. 人没有办法只通过吸收变得更美好和丰富,它必须通过大胆的碰撞和创造.如果不能保持足够 ...

  • 【学习笔记】控制角色移动的N种方法,但都离不开重复执行

    [学习笔记]控制角色移动的N种方法,但都离不开重复执行 今天我们讲一下控制角色移动的多种方法,因为缺少操作实例,希望课下同学们结合例子好好练习. 首先,我们说一下控制角色移动的多种方法.最比较常见的就 ...

  • 胡希恕伤寒论学习笔记——42

    42.太阳病,外证未解,脉浮弱者,当以汗解,宜桂枝汤. 字面意思是说:太阳病,外证依然存在,脉是浮弱的,治疗上依然需要通过出汗的方法,这时应该用桂枝汤一类的方剂. "宜"字说明不是 ...