Angular 与 Component store实践示例

目录
  • 正文
  • 将逻辑部分分离到 service
  • component store 的使用方法
    • 在 component 中使用
    • 在 component 中对于 store 的使用

正文

我们知道,Angular 中因为有 servicerxjs 的存在,使得状态管理在 Angular,并非必须。

一个 BehaviorSubject 就形成了最简单的状态管理:

将逻辑部分分离到 service

使用 Rxjs 将 service 中的逻辑部分,拆分为状态和方法。Component subscribe state 来更新 view, 调用方法来修改 state。

我们知道,Angular 中可以使用 NgRx 提供的 store 来做状态管理。NgRx store 跟 Redux 本质上是一样的。多数时候,可能我们都觉得 Redux 太重了。而且强行将数据与 view 分离。

除了 ngrx store, ngrx 还提供了另外一种轻量级的状态管理,component store。本质上它跟我们刚刚介绍的状态管理模式是一致的,只是提供了一些接口方便我们使用。 NgRx - @ngrx/component-store

在定位上,可以参考 hooks 与 redux 的区分,Ngrx store 用来处理全局状态,Component Store 用来 component 内局部的状态管理使用。(Store 和 Component 拥有相同的生命周期)。当然,实际上使用下来,component store 也完全可以用来当作全局的状态管理来处理。

component store 的使用方法

我们可以看到,store 主要提供了三个方法:

  • select, 用来拆分 state
  • updater, 用来更新 state, 主要是无 effect 的 state 更新。
  • effect, 用来处理有 effect 的情况,调用 updater 的方法来更新数据。

我们可以看出来,这样的接口设计跟 Mobx 或者 Vuex 是比较接近的。区别就是,因为 RxJS 的缘故,它的实现异常简单。几乎只是基于 behaviorSubject 包了一层壳。

有两点还是值得一提的:

updater 和 effect 的方法参数可以同时接受 value 和 observable value, 这使得我们在操作一个 stream 的时候,可以直接将 stream 作为参数。

比如我们现在有一个方法 updateUserName: (string | Observable<strring>) => void;

使用的时候,我们可以直接调用:updateUserName('zhangsan')

有时候我们在 component 里拿到的是一个 stream。

比如 form.valueChanges, 这时候我们就不需要手动 subscribe stream, 而是直接

updateUserName(form.valueChanges.pipe(map(form => form.userName)))

  • updater 和 effect 将 stream 作为参数后会自动 subscribe 并在 store 被销毁的时候 unsubscribe, 这就意味着你不用再写一堆手动 unsubscribe 的逻辑。

在 component 中使用

在 component 中使用也比较简单:

  • 将 component 中必要的数据投喂给 store, 一般来说是 input.
  • 在 component 中调用 updater 或者 effect 返回的方法修改 state。
  • 在 component 中 subscribe state 实现 view 层的渲染。
@Component({
  template: `...`,
  // ️MoviesStore is provided higher up the component tree
})
export class MovieComponent {
  movie$: Observable<Movie>;
  @Input()
  set movieId(value: string) {
    // calls effect with value.  Notice it's a single string value.
    this.moviesStore.getMovie(value);
    this.movie$ = this.moviesStore.selectMovie(value);
  }
  constructor(private readonly moviesStore: MoviesStore) {}
}

当然,我们也可以做一点优化,比如,尽可能将逻辑放在 store 中 componet 只做简单的调用。将数据之间的联动关系放在 store 的 constructor 中,component 只做调用。

@Component({
  template: `...`,
  // ️MoviesStore is provided higher up the component tree
})
export class MovieComponent {
  movie$: Observable<Movie>;
  @Input()
  set movieId(value: string) {
    this.mobiesStore.patchState({movieId: value});
  }
  constructor(private readonly moviesStore: MoviesStore) {}
}
@Injectable()
export class MoviesStore extends ComponentStore<MoviesState> {
  constructor(private readonly moviesService: MoviesService) {
    super({movieId: string; movies: []});
    this.geMovie(this.movieId$);
  }
  movieId$ = this.select(state => state.movieId);
  movie$ = this.moviesStore.selectMovie(this.movieId$);
  readonly getMovie = this.effect((movieId$: Observable<string>) => {
    //....
  });
  readonly addMovie = this.updater((state, movie: Movie) => ({
    // ...
  }));
  selectMovie(movieId: string) {
     // ...
  }
}

因为这里的 component store 是针对单个 component 的,也就是通常情况它的使用场景是逻辑较为复杂的 component。一个 component 基于 input 的变化完全可以转化为对于 store 的监听。那么,我们基本上可以将 component 的多数 input 同步到 store 中。

在一段时间的使用过程中,我发现,这是比较费劲的,

  • 同一个字段,我需要在 component 和 store state 中声明两次。
  • Input 必须转写成 set 模式
比如 userName 这个字段
原来:
@Input userName: string;
与 store 同步:
@Input
set userName(val: string) {
  this.store.patchState({userName: val});
}
如果想在 component 中直接调用 userName 就更麻烦了。
private _userName: string;
@Input
set userName(val: string) {
  this._userName = val;
  this.store.patchState({userName: val});
}
get userName() {
  return this._userName;
}

如果字段比较多,简直就是灾难。

最近在尝试一种不同于官网推荐的方法。我们知道,除了 set 我们还有一种更为常规的方法获取 input changes, 那就是 ngChanges。

export function mapPropChangesToStore<T extends JsonRecord>(this: ComponentStore<T>, mappedKeys: readonly string[], changes: SimpleChanges) {
  const state = mappedKeys.reduce((prev: Partial<T>, propKey) => {
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const propValue = changes?.[propKey];
    if (!propValue) {
      return prev;
    }
    return ({
      ...prev,
      [propKey]: propValue.currentValue,
    });
  }, {});
  if (isEmpty(state)) {
    return;
  }
  this.patchState(state);
}

在 component 中对于 store 的使用

import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { mapPropChangesToStore } from '@dashes/ngx-shared';
import { componentInputs, CsDemoStore } from './cs-demo.store';
@Component({
  selector: 'app-cs-demo',
  templateUrl: './cs-demo.component.html',
  styleUrls: ['./cs-demo.component.css']
})
export class CsDemoComponent implements OnChanges {
  @Input() p1!: string;
  @Input() p2!: string;
  @Input() p3!: string;
  constructor(public store: CsDemoStore) { }
  ngOnChanges(changes: SimpleChanges): void {
    mapPropChangesToStore.bind(this.store)(componentInputs, changes);
  }
}
export const componentInputs = ['p1', 'p2'] as const;
export type State = Pick&lt;CsDemoComponent, typeof componentInputs[number]&gt;;

这样看起来就会简洁很多。

以上就是Angular 与 Component store实践示例的详细内容,更多关于Angular Component store的资料请关注我们其它相关文章!

(0)

相关推荐

  • angular-tree-component的使用详解

    先上网址吧:https://github.com/500tech/angular-tree-component 这是牛逼哄哄的GitHub页面, http://500tech.github.io/angular-tree-component/ 这就是官网啦. 大背景--首先我是在Angular4下面使用的. 1.install from npm : npm install --save angular-tree-component 2.导入css 在styles.scss下面导入样式: @imp

  • Angular入口组件(entry component)与声明式组件的区别详解

    前言 组件是Angular中很重要的一部分,下面这篇文章就来给大家介绍关于Angular入口组件(entry component)与声明式组件的区别,Angular的声明式组件和入口组件的区别体现在两者的加载方式不同. 声明式组件是通过组件声明的selector加载 入口组件(entry component)是通过组件的类型动态加载 声明式组件 声明式组件会在模板里通过组件声明的selector加载组件. 示例 @Component({ selector: 'a-cmp', template:

  • 剖析Angular Component的源码示例

    Web Component 在介绍Angular Component之前,我们先简单了解下W3C Web Components 定义 W3C为统一组件化标准方式,提出Web Component的标准. 每个组件包含自己的html.css.js代码. Web Component标准包括以下四个重要的概念: 1.Custom Elements(自定义标签):可以创建自定义 HTML 标记和元素: 2.HTML Templates(HTML模版):使用 <template> 标签去预定义一些内容,但

  • AngularJs Understanding the Controller Component

    在angular中,controller是一个javascript 函数(type/class),被用作扩展除了root scope在外的angular scope(http://www.jb51.net/article/91749.htm)的实例.当我们或者angular通过scope.$new API(http://docs.angularjs.org/api/ng.$rootScope.Scope#$new)创建新的child scope时,有一个选项作为方法的参数传入controller

  • AngularJs Understanding the Model Component

    在angular文档讨论的上下文中,术语"model"可以适用于单一对象代表一个实体(例如,一个叫" phones"的model,它的值是一个电话数组.)或者作为应用的全部数据Model(所有实体). 在angular中,model可以是任意数据,可以通过angular的scope对象的属性来获取model.属性的名称是model的标识,值可以是任意javascript对象(包括数组和原始数据). javascript想成为model的唯一的条件是对象必须作为一个s

  • Angular 与 Component store实践示例

    目录 正文 将逻辑部分分离到 service component store 的使用方法 在 component 中使用 在 component 中对于 store 的使用 正文 我们知道,Angular 中因为有 service 和 rxjs 的存在,使得状态管理在 Angular,并非必须. 一个 BehaviorSubject 就形成了最简单的状态管理: 将逻辑部分分离到 service 使用 Rxjs 将 service 中的逻辑部分,拆分为状态和方法.Component subscri

  • react后台系统最佳实践示例详解

    目录 一.中后台系统的技术栈选型 1. 要做什么 2. 要求 3. 技术栈怎么选 二.hooks时代状态管理库的选型 context redux recoil zustand MobX 三.hooks的使用问题与解决方案 总结 一.中后台系统的技术栈选型 本文主要讲三块内容:中后台系统的技术栈选型.hooks时代状态管理库的选型以及hooks的使用问题与解决方案. 1. 要做什么 我们的目标是搭建一个适用于公司内部中后台系统的前端项目最佳实践. 2. 要求 由于业务需求比较多,一名开发人员需要负

  • Immer 功能最佳实践示例教程

    目录 一.前言 二.学习前提 三.历史背景 四.immer 功能介绍 好处 更新模式 更新对象 更新数组 嵌套数据结构 异步 producers & createDraft createDraft 和 finishDraft 五.性能提示 预冻结数据 可以随时选择退出 对于性能消耗大的的搜索操作,从原始 state 读取,而不是 draft 将 produce 拉到尽可能远的地方 六.陷阱 不要重新分配 recipe 参数 Immer 只支持单向树 永远不要从 producer 那里显式返回 u

  • Android neon 优化实践示例

    目录 搭建实验环境 小试牛刀 实战尝试 CMake添加汇编支持 实现汇编Neon优化 搭建实验环境 首先新建一个包含native代码的项目: 然后在gradle中添加对neon的支持: externalNativeBuild { cmake { cppFlags "-std=c++14" arguments "-DANDROID_ARM_NEON=TRUE" } } 这样,项目就可以支持neon加速了. 小试牛刀 一个最简单的neon编程的流程大致是这样的: 1.装

  • 微服务架构之服务注册与发现实践示例详解

    目录 1 服务注册中心 4种注册中心技术对比 2 Spring Cloud 框架下实现 2.1 Spring Cloud Eureka 2.1.1 创建注册中心 2.1.2 创建客户端 2.2 Spring Cloud Consul 2.2.1 Consul 的优势 2.2.2 Consul的特性 2.2.3 安装Consul注册中心 2.2.4 创建服务提供者 3 总结 微服务系列前篇 详解微服务架构及其演进史 微服务全景架构全面瓦解 微服务架构拆分策略详解 微服务架构之服务注册与发现功能详解

  • Spring Data Jpa框架最佳实践示例

    目录 前言 扩展接口用法 SPRINGDATAJPA最佳实践 一.继承SIMPLEJPAREPOSITORY实现类 二.集成QUERYDSL结构化查询 1.快速集成 2.丰富BaseJpaRepository基类 3.最终的BaseJpaRepository形态 三.集成P6SPY打印执行的SQL 结语 前言 Spring Data Jpa框架的目标是显著减少实现各种持久性存储的数据访问层所需的样板代码量. Spring Data Jpa存储库抽象中的中央接口是Repository.它需要领域实

  • spring boot集成redisson的最佳实践示例

    目录 前言 集成jedis实例,xml方式 集成前引用的jar springbean配置xml 集成redisson实例,javabean的方式 集成前引入的jar javabean配置如下 提供实例化javabean application.properties添加如下配置 前言 本文假使你了解spring boot并实践过,非spring boot用户可跳过也可借此研究一下. redisson是redis的java客户端程序,国内外很多公司都有在用,如下, 和spring的集成中官方给出的实

  • golang组件swagger生成接口文档实践示例

    目录 swagger介绍 gin-swagger实战 第一步:添加注释 第二步:生成接口文档数据 第三步:引入gin-swagger渲染文档数据 swagger介绍 Swagger本质上是一种用于描述使用JSON表示的RESTful API的接口描述语言.Swagger与一组开源软件工具一起使用,以设计.构建.记录和使用RESTful Web服务.Swagger包括自动文档,代码生成和测试用例生成. 在前后端分离的项目开发过程中,如果后端同学能够提供一份清晰明了的接口文档,那么就能极大地提高大家

  • 实践示例理解js强缓存协商缓存

    目录 背景 前置准备 准备 启动页面 HTTP缓存种类 强缓存 expires cache-control 协商缓存 Last-Modified,If-Modified-Since Etag,If-None-Match 总结 背景 无论是开发中或者是面试中,HTTP缓存都是非常重要的,这体现在了两个方面: 开发中:合理利用HTTP缓存可以提高前端页面的性能 面试中:HTTP缓存是面试中的高频问点 所以本篇文章,我不讲废话,我就通过Nodejs的简单实践,给大家讲最通俗易懂的HTTP缓存,大家通过

  • iOS无障碍适配西瓜视频Voice Over实践示例

    目录 一.Voice Over 简介 二.Voice Over 使用指南 Voice Over 开发环境配置 Voice Over 基本使用 —— 以西瓜为例 入门手势 进阶手势 三.快速适配 Voice Over / 无障碍 设置无障碍焦点 设置无障碍文案 调整焦点顺序 西瓜首页适配实战 搜索栏 频道栏 & 频道编辑器 作者动态栏 视频列表 四.Voice Over 相关协议介绍 UIAccessibility 无障碍标签标注 UIAccessibilityAction 无障碍手势响应 UIA

随机推荐