使用具有独立 Angular 功能的 NgRx 包

2025-06-10

使用具有独立 Angular 功能的 NgRx 包

⚠️ 本文撰写于我们发布独立 NgRx API 之前。它们从 v14.3.0 开始可用。点击此处了解更多信息。

在本文中,我们将研究版本 14 中引入的独立 Angular API。然后,我们将探索如何使用具有独立功能的 NgRx 包。

内容


独立的 Angular API

借助独立的 Angular API,我们可以无需 NgModule 即可构建 Angular 应用。换句话说,组件、指令和管道无需在任何 Angular 模块中声明即可使用。

💡 在 Angular 14 中,独立 API 处于开发人员预览阶段,将来可能会发生变化,并且不具有向后兼容性。

创建独立组件

要创建独立组件,我们需要将standalone标志设置为,并使用组件配置中的属性true注册模板依赖项。该数组可以接受 Angular 模块或其他独立组件、指令或管道:importsimports

// header.component.ts

@Component({
  selector: 'app-header',
  template: `
    <a routerLink="/">Home</a>
    <a *ngIf="isAuthenticated$ | async" routerLink="/">Musicians</a>
  `,
  standalone: true,
  // importing modules whose declarables are used in the template
  imports: [CommonModule, RouterModule],
})
export class HeaderComponent {
  readonly isAuthenticated$ = this.authService.isAuthenticated$;

  constructor(private readonly authService: AuthService) {}
}

// app.component.ts

@Component({
  selector: 'app-root',
  template: `
    <app-header></app-header>
    <router-outlet></router-outlet>
  `,
  standalone: true,
  // importing `HeaderComponent` as a template dependency
  imports: [RouterModule, HeaderComponent],
})
export class AppComponent {}
Enter fullscreen mode Exit fullscreen mode

AppModule不再需要引导应用程序。相反,我们可以使用bootstrapApplication包中@angular/platform-browser接受根组件作为输入参数的函数:

// main.ts

bootstrapApplication(AppComponent);
Enter fullscreen mode Exit fullscreen mode

bootstrapApplication函数接受一个带有提供者的对象作为第二个参数,因此我们可以在根级别提供服务,如下所示:

bootstrapApplication(AppComponent, {
  providers: [
    { provide: ErrorHandler, useClass: CustomErrorHandler },
  ],
});
Enter fullscreen mode Exit fullscreen mode

与 Angular 模块互操作

现在的问题是,如何从现有的 Angular 模块提供服务。幸运的是,importProvidersFrom@angular/core包中新增了一个函数,它接受一系列 Angular 模块作为输入参数,并返回它们的提供程序:

const providers = importProvidersFrom(
  HttpClientModule,
  // ... other modules
);
Enter fullscreen mode Exit fullscreen mode

函数返回的提供程序importProvidersFrom可以通过以下方式在根级别注册:

bootstrapApplication(AppComponent, {
  providers: [
    importProvidersFrom(HttpClientModule),
  ],
});
Enter fullscreen mode Exit fullscreen mode

配置 Angular 路由器

在 Angular 14 中,可以通过将providers数组添加到Route对象来在路由级别注册提供程序。这样就可以按照以下方式定义功能级提供程序:

// musicians.routes.ts

export const musiciansRoutes: Route[] = [
  {
    path: '',
    // registering providers for the route and all its children
    providers: [
      { provide: MusiciansService, useClass: MusiciansHttpService },
      importProvidersFrom(NgModule1, NgModule2),
    ],
    children: [
      {
        path: '',
        component: MusicianListComponent,
      },
      {
        path: ':id',
        component: MusicianDetailsComponent,
        canActivate: [MusicianExistsGuard],
      },
    ],
  },
];
Enter fullscreen mode Exit fullscreen mode

然后,我们可以使用loadChildren应用程序路由配置中的属性来延迟加载功能路由:

// app.routes.ts

export const appRoutes: Route[] = [
  { path: '', component: HomeComponent },
  {
    path: 'musicians',
    // importing `musiciansRoutes` using the `loadChildren` property
    loadChildren: () =>
      import('@musicians/musicians.routes').then(
        (m) => m.musiciansRoutes
      ),
  },
];
Enter fullscreen mode Exit fullscreen mode

下一步是使用RouterModule以下命令注册应用程序路由:

// main.ts

bootstrapApplication(AppComponent, {
  providers: [
    importProvidersFrom(RouterModule.forRoot(appRoutes)),
  ],
});
Enter fullscreen mode Exit fullscreen mode

在引导应用程序时,Angular 将初始化根RouterModule,注册应用程序路由,并在根级别提供RouterActivatedRoute和其他提供程序。RouterModule


NgRx 包中的 Angular 模块

正如我们在 示例中所见RouterModule,Angular 模块不仅用于声明组件或提供服务,还用于配置各种应用程序和库功能。在 NgRx 中,我们使用EffectsModule.forRoot方法来在 Angular 应用程序的根级别提供Actions可观察对象,初始化效果运行器并运行根效果。因此,从其他 NgRx 包导入根模块将配置其功能和/或提供服务:

// app.module.ts

@NgModule({
  imports: [
    // provide `Store` at the root level
    // register initial reducers
    // initialize runtime checks mechanism
    StoreModule.forRoot({ router: routerReducer, auth: authReducer }),
    // connect NgRx Store with Angular Router
    StoreRouterConnectingModule.forRoot(),
    // connect NgRx Store with Redux Devtools extension
    StoreDevtoolsModule.instrument(),
    // provide `Actions` at the root level
    // initialize effects runner
    // run root effects
    EffectsModule.forRoot([RouterEffects, AuthEffects]),
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

此外,NgRx 还公开了用于在功能模块中注册其他 reducer 和效果的 API:

// musicians.module.ts

@NgModule({
  imports: [
    // register feature reducer
    StoreModule.forFeature('musicians', musiciansReducer),
    // run feature effects
    EffectsModule.forFeature([MusiciansApiEffects]),
  ],
})
export class MusiciansModule {}
Enter fullscreen mode Exit fullscreen mode

将 NgRx 模块与独立 Angular API 结合使用

与 root 类似RouterModule,可以使用以下函数在应用程序级别配置 NgRx 模块bootstrapApplication

// main.ts

bootstrapApplication(AppComponent, {
  providers: [
    importProvidersFrom(
      RouterModule.forRoot(appRoutes),

      // configure NgRx modules
      StoreModule.forRoot({
        router: routerReducer,
        auth: authReducer,
      }),
      StoreRouterConnectingModule.forRoot(),
      StoreDevtoolsModule.instrument(),
      EffectsModule.forRoot([RouterEffects, AuthEffects])
    ),
  ],
});
Enter fullscreen mode Exit fullscreen mode

可以在路由配置中针对特定功能延迟注册功能减速器和效果,如下所示:

// musicians.routes.ts

export const musiciansRoutes: Route[] = [
  {
    path: '',
    providers: [
      importProvidersFrom(
        // register feature reducer
        StoreModule.forFeature('musicians', musiciansReducer),
        // run feature effects
        EffectsModule.forFeature([MusiciansApiEffects])
      ),
    ],
    children: [
      {
        path: '',
        component: MusicianListComponent,
      },
      {
        path: ':id',
        component: MusicianDetailsComponent,
        canActivate: [MusicianExistsGuard],
      },
    ],
  },
];
Enter fullscreen mode Exit fullscreen mode

独立的 NgRx API

我们可以使用函数来获得“无模块”的开发体验,而不是使用 NgModules 来配置 NgRx 包和/或提供其服务。例如,我们可以使用名为 的函数,provideStore而不是StoreModule.forRoot。同样的原则也适用于其他 NgRx 包。使用独立的 NgRx 函数如下所示:

// main.ts

bootstrapApplication(AppComponent, {
  providers: [
    // alternative to `StoreModule.forRoot`
    provideStore({ router: routerReducer, auth: AuthReducer }),
    // alternative to `StoreRouterConnectingModule.forRoot`
    provideRouterStore(),
    // alternative to `StoreDevtoolsModule.instrument`
    provideStoreDevtools(),
    // alternative to `EffectsModule.forRoot`
    provideEffects([RouterEffects, AuthEffects]),
  ),
});
Enter fullscreen mode Exit fullscreen mode

功能缩减器和效果也将使用函数而不是 NgModules 进行注册:

// musicians.routes.ts

export const musiciansRoutes: Route[] = [
  {
    path: '',
    providers: [
      // alternative to `StoreModule.forFeature`
      provideStoreFeature('musicians', musiciansReducer),
      // alternative to `EffectsModule.forFeature`
      provideFeatureEffects([MusiciansApiEffects]),
    ],
    children: [
      {
        path: '',
        component: MusicianListComponent,
      },
      {
        path: ':id',
        component: MusicianDetailsComponent,
        canActivate: [MusicianExistsGuard],
      },
    ],
  },
];
Enter fullscreen mode Exit fullscreen mode

💡 独立 NgRx API 的设计仍在考虑中。如果您有任何建议,请在此处留言。

源代码

建议的独立 NgRx API 和示例项目的源代码可在此处获得。

资源

同行评审员

非常感谢Tim DeschryverBrandon Roberts审阅本文!

鏂囩珷鏉ユ簮锛�https://dev.to/ngrx/using-ngrx-packages-with-standalone-angular-features-53d8
PREV
我如何用几十行 Ruby 代码替换 Rails 应用
NEXT
宣布 NgRx Signals v18:状态封装、私有存储成员、增强实体管理等等!