首页 > 解决方案 > 惰性功能模块架构迁移到 NGRX:Angular 8

问题描述

嘿伙计们,目前我正在一个应用程序中工作,实现了延迟功能模块架构。您可以在下面看到项目的结构。

在此处输入图像描述

由于应用程序不断增长,我们决定将其迁移到 Ngrx。

对我来说这是一种新模式,我正在寻找迁移指南,但我只能在从头开始创建项目时找到 ngrx 指南。

您能否给我一些提示、指导方针、我应该在哪里小心,以及可能的一些步骤摘要?

谢谢。

标签: angularngrx

解决方案


指导方针

可以延迟加载您的商店,但这给我带来的问题多于好处。例如,根据路由器 url 和加载的项目实体获取一个选定的项目,该项目实体混合了两个特征存储。以下文章为我提供了一种拆分存储的好方法,同时允许应用程序的任何部分访问存储数据:

https://itnext.io/ngrx-best-practices-for-enterprise-angular-applications-6f00bcdf36d7

对于后代,应用程序结构如下所示:

├── app
 │ ├── app-routing.module.ts
 │ ├── app.component.css
 │ ├── app.component.html
 │ ├── app.component.ts
 │ ├── app.module.ts
 │ ├── components
 │ ├── containers
 │ │    └── my-feature
 │ │         ├── my-feature.component.css
 │ │         ├── my-feature.component.html
 │ │         └── my-feature.component.ts
 │ ├── models
 │ │    ├── index.ts
 │ │    └── my-model.ts
 │ │    └── user.ts
 │ ├── root-store
 │ │    ├── index.ts
 │ │    ├── root-store.module.ts
 │ │    ├── selectors.ts
 │ │    ├── state.ts
 │ │    └── my-feature-store
 │ │    |    ├── actions.ts
 │ │    |    ├── effects.ts
 │ │    |    ├── index.ts
 │ │    |    ├── reducer.ts
 │ │    |    ├── selectors.ts
 │ │    |    ├── state.ts
 │ │    |    └── my-feature-store.module.ts
 │ │    └── my-other-feature-store
 │ │         ├── actions.ts
 │ │         ├── effects.ts
 │ │         ├── index.ts
 │ │         ├── reducer.ts
 │ │         ├── selectors.ts
 │ │         ├── state.ts
 │ │         └── my-other-feature-store.module.ts
 │ └── services
 │      └── data.service.ts
 ├── assets
 ├── browserslist
 ├── environments
 │ ├── environment.prod.ts
 │ └── environment.ts
 ├── index.html
 ├── main.ts
 ├── polyfills.ts
 ├── styles.css
 ├── test.ts
 ├── tsconfig.app.json
 ├── tsconfig.spec.json
 └── tslint.json

我应该在哪里小心

确保你的 reducer 返回未定义操作的未修改状态。您可以对此进行测试。没有理由不测试你的减速器。这些是纯函数,易于测试。

import * as fromProjects from './project.reducer'
import * as fromProjectState from './project.state'

describe('ProjectReducer', () => {
  describe('undefined action', () => {
    it('should return the default state', () => {
      const { initialState } = fromProjectState
      const action = {} as any
      const state = fromProjects.reducer(initialState, action)

      expect(state).toBe(initialState)
    })
  })
})

花时间确保您的操作类型正确 - 错误很难调试。由于样板文件,您可能会在此处复制并粘贴大量代码。再次,这可以被测试。

describe('LoadProjectsFail', () => {
  it('should create an action', () => {
    const payload = { message: 'Load Error ' }
    const action = new fromProjects.LoadProjectsFail(payload)
    expect({ ...action }).toEqual({
      type: fromProjects.LOAD_PROJECTS_FAIL,
      payload,
    })
  })
})

遵守 NgRx 文档 - 有一些更改,教程通常至少落后一个版本。例如

this.store.pipe(select(projectSelectors.getProjectsLoading))
// versus
this.store.select(projectSelectors.getProjectsLoading)

步骤总结

与链接几乎相同,但顺序不同:

根存储

  1. root-store.module.ts
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { EffectsModule } from '@ngrx/effects';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools'
import { environment } from 'src/environments/environment'
// import { MyFeatureStoreModule } from './my-feature-store/';
// import { MyOtherFeatureStoreModule } from './my-other-feature-store/';

@NgModule({
  imports: [
    CommonModule,
    // MyFeatureStoreModule,
    // MyOtherFeatureStoreModule,
    StoreModule.forRoot({}),
    EffectsModule.forRoot([])
    // Store devtools
    !environment.production
      ? StoreDevtoolsModule.instrument({
          name: 'My App',
        })
      : [],
  ],
  declarations: []
})
export class RootStoreModule {}

添加以下文件,这些文件开始时几乎是空的:

  • index.ts

    import { RootStoreModule } from './root-store.module'
    import * as RootStoreState from './state'
    import * as RootStoreSelectors from './selectors'
    //export * from './employee'
    //export * from './project'
    //export * from './router'
    export { RootStoreState, RootStoreSelectors, RootStoreModule }

  • state.ts

    import { routerState } from './router'
    import { employeeState } from './employee'
    import { projectState } from './project'

    export interface State {
      router: routerState.State
      employees: employeeState.State
      projects: projectState.State
    }

  • selectors.ts

    import { createSelector } from '@ngrx/store'
    import { Project } from './project/project.model'
    import { routerSelectors } from './router'
    import { projectSelectors } from './project'

    export const getSelectedProject = createSelector(
      projectSelectors.getProjectsEntities,
      routerSelectors.getRouterState,
      (entities, router): Project => {
        return router.state && entities[router.state.params.id]
      }
    )

  1. 导入RootStoreModuleapp.module.ts

特色商店

  1. 定义您的功能状态
  2. 定义特征动作
  3. 写出你的特征效果
  4. 编写你的特征缩减器(如果可能,首先测试)
  5. 编写您的功能模块
  6. 写你的index.ts,添加到rootindex.ts
  7. 将特征状态添加到根状态
  8. 将功能模块声明为根存储模块的一部分。
  9. 定义特征选择器
  10. 定义任何根选择器(混合特征选择器)

堆栈闪电战


推荐阅读