zoukankan      html  css  js  c++  java
  • ngRx 官方示例分析

    我们从 Action 名称开始。

    解决 Action 名称冲突问题

    ngRx 中,不同的 Action 需要一个 Action Type 进行区分,一般来说,这个 Action Type 是一个字符串,如何定义和使用这个字符串是需要首先考虑的问题。需要保证不同的 Action 名称不能冲突,使用的时候还需要方便,编码的时候,最好有提示等等。

    首先处理命名冲突问题,示例使用 util 中定义的一个字典来检查是否已经定义了一个 Action

    app/util.ts

    /**
     * This function coerces a string into a string literal type.
     * Using tagged union types in TypeScript 2.0, this enables
     * powerful typechecking of our reducers.
     *
     * Since every action label passes through this function it
     * is a good place to ensure all of our action labels
     * are unique.
     */
    
    const typeCache: { [label: string]: boolean } = {};
    export function type<T>(label: T | ''): T {
      if (typeCache[<string>label]) {
        throw new Error(`Action type "${label}" is not unique"`);
      }
    
      typeCache[<string>label] = true;
    
      return <T>label;
    }

    使用 TypeScript 的 Playground 翻译一下,可以得到如下的结果:

    var typeCache = {};
    function type(label) {
        if (typeCache[label]) {
             throw new Error("Action type "" + label + "" is not unique"");
        }
        typeCache[label] = true;
        return label;
    }

    可以更加直观地看到,这个 type 函数可以接收一个字符串,在类型缓存对象 typeCache 中检查是否已经设置过这个 Key, 如果已经设置过一次,抛出异常,这样可以避免命名冲突问题。如果没有,则将这个 Action Type 保存为缓存对象的键,值设置为 true. 最后返回这个 Action Type 的字符串。特别需要注意的是它已经被作为一个类型返回了。

    使用 String Literal Type 实现 Action 名称的强类型化

    具体的 Action Type 有哪些呢?它们分别定义在 /app/actions/books.ts/app/actions/layouts.ts 和 /app/actions/collection.ts 中。 

     实现 Action 接口

    然后,我们再看看 Action 接口的定义,它来自 @ngrx/store

    export interface Action {
      readonly type: string;
    }

    这里是对一个 Action 基本的要求,需要一个字符串类型的 Action 名称。

    为了便于使用每一种 Action ,使用了 Action Creator。以后可以直接使用这些 class 来创建 Action,而不用直接创建对象。这里定义了每种 Action 所对应的 Action 实现。例如 SearchAction 的实现如下所示:

    /**
     * Every action is comprised of at least a type and an optional
     * payload. Expressing actions as classes enables powerful
     * type checking in reducer functions.
     *
     * See Discriminated Unions: https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions
     */
    export class SearchAction implements Action {
      type = ActionTypes.SEARCH;
    
      constructor(public payload: string) { }
    }

    可以看到它的 Action 类型使用 ActionTypes 来定义,值固定设置为了 "[Book] Search",可以通过构造函数传递这个 Action 所使用的附加数据。

    ActionTypes 的定义如下所示,实际上是一个对象。我们通过属性来获取实际的值,这个值来自前面的 type 函数返回值。

    /**
     * For each action type in an action group, make a simple
     * enum object for all of this group's action types.
     *
     * The 'type' utility function coerces strings into string
     * literal types and runs a simple check to guarantee all
     * action types in the application are unique.
     */
    export const ActionTypes = {
      SEARCH:           type('[Book] Search'),
      SEARCH_COMPLETE:  type('[Book] Search Complete'),
      LOAD:             type('[Book] Load'),
      SELECT:           type('[Book] Select'),
    };

    其它三种的 Action 以此类推。

     我们先看看最后的 5 行。这里导出类型的别名。

    这里使用了 TypeScript 中的 String Literal Types, 使得我们可以利用强类型的方式来使用字符串。

    /**
     * Export a type alias of all actions in this action group
     * so that reducers can easily compose action types
     */
    export type Actions
      = SearchAction
      | SearchCompleteAction
      | LoadAction
      | SelectAction;

    代码实现

    代码的全部内容如下所示。

    import { Action } from '@ngrx/store';
    import { Book } from '../models/book';
    import { type } from '../util';
    
    /**
     * For each action type in an action group, make a simple
     * enum object for all of this group's action types.
     *
     * The 'type' utility function coerces strings into string
     * literal types and runs a simple check to guarantee all
     * action types in the application are unique.
     */
    export const ActionTypes = {
      SEARCH:           type('[Book] Search'),
      SEARCH_COMPLETE:  type('[Book] Search Complete'),
      LOAD:             type('[Book] Load'),
      SELECT:           type('[Book] Select'),
    };
    
    
    /**
     * Every action is comprised of at least a type and an optional
     * payload. Expressing actions as classes enables powerful
     * type checking in reducer functions.
     *
     * See Discriminated Unions: https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions
     */
    export class SearchAction implements Action {
      type = ActionTypes.SEARCH;
    
      constructor(public payload: string) { }
    }
    
    export class SearchCompleteAction implements Action {
      type = ActionTypes.SEARCH_COMPLETE;
    
      constructor(public payload: Book[]) { }
    }
    
    export class LoadAction implements Action {
      type = ActionTypes.LOAD;
    
      constructor(public payload: Book) { }
    }
    
    export class SelectAction implements Action {
      type = ActionTypes.SELECT;
    
      constructor(public payload: string) { }
    }
    
    /**
     * Export a type alias of all actions in this action group
     * so that reducers can easily compose action types
     */
    export type Actions
      = SearchAction
      | SearchCompleteAction
      | LoadAction
      | SelectAction;

    然后是 /app/actions/layouts.ts.

    import { Action } from '@ngrx/store';
    import { type } from '../util';
    
    export const ActionTypes = {
      OPEN_SIDENAV:   type('[Layout] Open Sidenav'),
      CLOSE_SIDENAV:  type('[Layout] Close Sidenav')
    };
    
    
    export class OpenSidenavAction implements Action {
      type = ActionTypes.OPEN_SIDENAV;
    }
    
    export class CloseSidenavAction implements Action {
      type = ActionTypes.CLOSE_SIDENAV;
    }
    
    
    export type Actions
      = OpenSidenavAction
      | CloseSidenavAction;

    以及 /app/actions/collection.ts 的定义。

    import { Action } from '@ngrx/store';
    import { Book } from '../models/book';
    import { type } from '../util';
    
    export const ActionTypes = {
      ADD_BOOK:             type('[Collection] Add Book'),
      ADD_BOOK_SUCCESS:     type('[Collection] Add Book Success'),
      ADD_BOOK_FAIL:        type('[Collection] Add Book Fail'),
      REMOVE_BOOK:          type('[Collection] Remove Book'),
      REMOVE_BOOK_SUCCESS:  type('[Collection] Remove Book Success'),
      REMOVE_BOOK_FAIL:     type('[Collection] Remove Book Fail'),
      LOAD:                 type('[Collection] Load'),
      LOAD_SUCCESS:         type('[Collection] Load Success'),
      LOAD_FAIL:            type('[Collection] Load Fail'),
    };
    
    /**
     * Add Book to Collection Actions
     */
    export class AddBookAction implements Action {
      type = ActionTypes.ADD_BOOK;
    
      constructor(public payload: Book) { }
    }
    
    export class AddBookSuccessAction implements Action {
      type = ActionTypes.ADD_BOOK_SUCCESS;
    
      constructor(public payload: Book) { }
    }
    
    export class AddBookFailAction implements Action {
      type = ActionTypes.ADD_BOOK_FAIL;
    
      constructor(public payload: Book) { }
    }
    
    /**
     * Remove Book from Collection Actions
     */
    export class RemoveBookAction implements Action {
      type = ActionTypes.REMOVE_BOOK;
    
      constructor(public payload: Book) { }
    }
    
    export class RemoveBookSuccessAction implements Action {
      type = ActionTypes.REMOVE_BOOK_SUCCESS;
    
      constructor(public payload: Book) { }
    }
    
    export class RemoveBookFailAction implements Action {
      type = ActionTypes.REMOVE_BOOK_FAIL;
    
      constructor(public payload: Book) { }
    }
    
    /**
     * Load Collection Actions
     */
    export class LoadAction implements Action {
      type = ActionTypes.LOAD;
    
      constructor() { }
    }
    
    export class LoadSuccessAction implements Action {
      type = ActionTypes.LOAD_SUCCESS;
    
      constructor(public payload: Book[]) { }
    }
    
    export class LoadFailAction implements Action {
      type = ActionTypes.LOAD_FAIL;
    
      constructor(public payload: any) { }
    }
    
    export type Actions
      = AddBookAction
      | AddBookSuccessAction
      | AddBookFailAction
      | RemoveBookAction
      | RemoveBookSuccessAction
      | RemoveBookFailAction
      | LoadAction
      | LoadSuccessAction
      | LoadFailAction;

     总结

    • Action 的名称使用了 String Literal Type 来实现强类型支持
    • 针对每种 Action 实现了 Action 接口,其中固定了所对应的 Action 类型,使用中不必再提供这个 Action 串,实际上是 Action Creator。
    • 对于应用中常见的大量 Action 名称冲突问题,通过 type 函数解决,这个函数将会缓存已经定义的 Action 类型。 

    参考资源:

    上一篇:ngRx 官方示例分析 - 1. 介绍

    下一篇:ngRx 官方示例分析 - 3. reducers

  • 相关阅读:
    __slots__魔法,减少实例属性消耗的内存
    在函数中最好不要用可变类型当参数
    Python的容器模块
    实例和类变量以及类的魔术方法
    推导式
    Python内置函数
    常用的git操作(持续更新)
    h开头的
    e开头的
    如何迁移测试的MAGENTO到正式运行的MAGENTO
  • 原文地址:https://www.cnblogs.com/haogj/p/6537064.html
Copyright © 2011-2022 走看看