使用具有独立 Angular 功能的 NgRx 包
⚠️ 本文撰写于我们发布独立 NgRx API 之前。它们从 v14.3.0 开始可用。点击此处了解更多信息。
在本文中,我们将研究版本 14 中引入的独立 Angular API。然后,我们将探索如何使用具有独立功能的 NgRx 包。
内容
独立的 Angular API
借助独立的 Angular API,我们可以无需 NgModule 即可构建 Angular 应用。换句话说,组件、指令和管道无需在任何 Angular 模块中声明即可使用。
💡 在 Angular 14 中,独立 API 处于开发人员预览阶段,将来可能会发生变化,并且不具有向后兼容性。
创建独立组件
要创建独立组件,我们需要将standalone
标志设置为,并使用组件配置中的属性true
注册模板依赖项。该数组可以接受 Angular 模块或其他独立组件、指令或管道:imports
imports
// 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 {}
AppModule
不再需要引导应用程序。相反,我们可以使用bootstrapApplication
包中@angular/platform-browser
接受根组件作为输入参数的函数:
// main.ts
bootstrapApplication(AppComponent);
该bootstrapApplication
函数接受一个带有提供者的对象作为第二个参数,因此我们可以在根级别提供服务,如下所示:
bootstrapApplication(AppComponent, {
providers: [
{ provide: ErrorHandler, useClass: CustomErrorHandler },
],
});
与 Angular 模块互操作
现在的问题是,如何从现有的 Angular 模块提供服务。幸运的是,importProvidersFrom
该@angular/core
包中新增了一个函数,它接受一系列 Angular 模块作为输入参数,并返回它们的提供程序:
const providers = importProvidersFrom(
HttpClientModule,
// ... other modules
);
函数返回的提供程序importProvidersFrom
可以通过以下方式在根级别注册:
bootstrapApplication(AppComponent, {
providers: [
importProvidersFrom(HttpClientModule),
],
});
配置 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],
},
],
},
];
然后,我们可以使用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
),
},
];
下一步是使用RouterModule
以下命令注册应用程序路由:
// main.ts
bootstrapApplication(AppComponent, {
providers: [
importProvidersFrom(RouterModule.forRoot(appRoutes)),
],
});
在引导应用程序时,Angular 将初始化根RouterModule
,注册应用程序路由,并在根级别提供Router
、ActivatedRoute
和其他提供程序。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 {}
此外,NgRx 还公开了用于在功能模块中注册其他 reducer 和效果的 API:
// musicians.module.ts
@NgModule({
imports: [
// register feature reducer
StoreModule.forFeature('musicians', musiciansReducer),
// run feature effects
EffectsModule.forFeature([MusiciansApiEffects]),
],
})
export class MusiciansModule {}
将 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])
),
],
});
可以在路由配置中针对特定功能延迟注册功能减速器和效果,如下所示:
// 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],
},
],
},
];
独立的 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]),
),
});
功能缩减器和效果也将使用函数而不是 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],
},
],
},
];
💡 独立 NgRx API 的设计仍在考虑中。如果您有任何建议,请在此处留言。
源代码
建议的独立 NgRx API 和示例项目的源代码可在此处获得。
资源
同行评审员
非常感谢Tim Deschryver和Brandon Roberts审阅本文!
鏂囩珷鏉ユ簮锛�https://dev.to/ngrx/using-ngrx-packages-with-standalone-angular-features-53d8