欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Angular 15 独立组件详解

Angular 15 独立组件详解

2024/10/24 7:43:48 来源:https://blog.csdn.net/m0_54944506/article/details/143025930  浏览:    关键词:Angular 15 独立组件详解

前言

在 Angular 15 中,独立 APIs 正式稳定,开发人员可以使用独立组件开发各种组件、指令、管道和构建应用程序。独立 APIs 减少了在开发时对 ngModule 的依赖,提升了开发体验。
目前,组件库 ngx-tethys 紧跟 Angular 脚步,现在已支持独立组件。

什么是独立组件

独立组件提供了一种简化的方式来构建 Angular 应用程序。独立组件、指令和管道旨在通过减少对 NgModule 的需求来简化创作体验。现有应用程序可以选择性地以增量方式采用新的独立风格,而无需任何重大更改。

如何创建独立组件

NgModule 创建组件

回顾下在独立 APIs 之前,创建组件、指令、管道需要完成哪些:
创建组件、指令、管道
创建 ngModule ,在此 ngModule 中需要:
通过 declarations 声明新创建的组件、指令和管道
通过 imports 导入在该模块中使用到的其他组件、指令、管道所属的模块
通过 exports 导出可供其他模块使用的组件、指令和管道
通过 providers 设置一些供组件使用的服务

比如创建一个按钮组件:
创建组件

@Component({selector: 'button',templateUrl: './button.component.html',changeDetection: ChangeDetectionStrategy.OnPush
})
export class ButtonComponent {}

定义 ButtonModule

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';import { ButtonComponent } from './button.component';@NgModule({declarations: [ButtonComponent,],imports: [CommonModule],exports: [ButtonComponent,],providers: []
})
export class ButtonModule {}

每次新增、修改组件时,都需要在 ngModule 中进行更改,过程比较繁琐。

独立 APIs 创建组件

使用独立 APIs 创建组件、指令、管道:
创建组件、指令、管道时,在元数据中配置 standalone: true
在元数据中 imports 使用的其他独立组件、模块

比如创建按钮组件:其中 NgIf 、 NgClass 为 Angular 提供的独立组件

@Component({selector: 'button',templateUrl: './button.component.html',changeDetection: ChangeDetectionStrategy.OnPush,standalone: true,imports: [NgIf, NgClass]
})
export class ButtonComponent {
}

回顾之前使用 NgModule 创建组件时,在模块定义中还会导出可用于其他模块/组件使用的组件列表,如果使用模块中多个组件,直接导入模块就行。使用独立组件如何设置一系列组件供其他模块/组件使用呢?

方式一:
定义一个 ngModule 作为多个独立组件的集合。此处需要注意的是:独立组件不可以再次在 ngModule 中声明。

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';import { ThyButtonComponent } from './button.component';@NgModule({imports: [CommonModule, ThyButtonComponent],exports: [ButtonComponent]
})
export class ButtonModule {}

方式二:
定义一个常量数组,将多个独立组件导出。此处需要注意的是: 常量需要 as const ,为 Angular 编译器提供了正确编译所需的额外信息。

export const BUTTON_COMPONENTS = [ThyButtonComponent] as const;

指令组合

如何使用
在指令支持独立指令后,独立指令还可以直接用于其他组件、指令,复用独立指令的逻辑。在指令或组件上使用 hostDirectives:[] ,将独立指令应用于组件。
如下示例, ThyFlex 为独立指令,指令有输入参数 thyDirection :

@Directive({selector: '[flex]',standalone: true,host: {class: 'd-flex'}
})
export class Flex implements OnInit, OnChanges {@Input() direction: FlexDirection;constructor() {}
}

组件 flex :

@Component({selector: 'flex',template: `<ng-content></ng-content>`,standalone: true,changeDetection: ChangeDetectionStrategy.OnPush,hostDirectives: [{directive: Flex,inputs: ['thyDirection: direction']}],imports: [Flex]
})
export class FlexComponent {}

需要注意的是: 默认情况下,指令Flex 的输入、输出参数不会作为组件ThyFlexComponent 的公开 API,需要如上显示的定义。
使用:

<flex thyDirection="xxx"></flex>

执行顺序
使用组合指令后,组件和宿主指令执行顺序:
宿主指令和直接在模板中使用的组件、指令会经历相同的生命周期。但是,宿主指令总是会在应用它们的组件或指令之前执行它们的构造函数、生命周期钩子和绑定。因此顺序为:
指令 Flex 实例化
组件 FlexComponent 实例化
指令 Flex 接收输入,执行 ngOninit
组件 FlexComponent 接收输入,执行 ngOninit
指令 Flex 应用宿主绑定
组件 FlexComponent 应用宿主绑定

依赖注入

使用了 hostDirectives 的组件或指令可以注入这些宿主指令的实例。

@Component({selector: 'flex',template: `<ng-content></ng-content>`,standalone: true,changeDetection: ChangeDetectionStrategy.OnPush,hostDirectives: [{directive: Flex,inputs: ['direction']}],imports: [Flex]
})
export class FlexComponent implements OnInit {private flexDirective = inject(Flex);constructor() {console.log('FlexComponent constructor');}ngOnInit(): void {console.log('FlexComponent ngOnInit');}
}

当把宿主指令应用于组件时,组件和宿主指令都可以定义提供者。如果带有 hostDirectives 的组件或指令以及这些宿主指令都提供相同的注入令牌,则带有 hostDirectives 的类定义的提供者会优先于宿主指令定义的提供者。

优势:
增强使用宿主指令的组件的功能;
增强代码的复用性

缺点:
过度使用宿主指令会影响应用程序的内存使用。

应用中使用

NgModule 启动应用

通过根模块启动应用,通过 bootstrapModule 启动,程序入口文件 main.ts 如下:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));

AppModule 中,除了上边提到的 ngModule 中配置,还会通过 providers 设置全局的依赖,通过 bootstrap: [ AppComponent ] 设置根组件。启动后,将根组件插入到 index.html 页面中,进而构建组件树( bootstrap 中支持配置多个组件,一般情况跟组件只会设置一个组件)。

@NgModule({declarations: [AppComponent],imports: [BrowserModule,AppRoutingModule],providers: [],bootstrap: [AppComponent]
})
export class AppModule { }

独立应用启动

通过使用独立组件作为应用程序的根组件,可以在没有任何 ​ NgModule ​的情况下引导 Angular 应用程序。使用 ​ bootstrapApplication ​启动应用,配置根组件(根组件为独立组件):
main.ts:

bootstrapApplication(AppComponent).catch(err => console.error(err));

AppComponent

@Component({selector: 'app-root',standalone: true,imports: [CommonModule, RouterOutlet, TopBarComponent],templateUrl: './app.component.html',styleUrls: ['./app.component.scss']
})
export class AppComponent {title = 'demo-standalone';
}

回顾使用 ngModule 启动应用时,除了指定启动模块、启动组件,还需要配置全局服务和路由。在使用独立 APIs 启动应用如何配置呢?
依赖
可以在启动应用程序时,配置全局依赖。

bootstrapApplication(AppComponent, {providers: [ProjectStore]
}).catch((err) => console.error(err));

如果第三方库仅支持 ngModule 模式配置依赖,可以使用 importProvidersFrom 配置。

import {LibraryModule} from 'ngmodule-based-library';bootstrapApplication(PhotoAppComponent, {providers: [importProvidersFrom(RouterModule.forRoot(routes))]
});

此外,Angular 其他模块或者第三方库也支持了以 provide-* 的方法配置依赖。

provideRouter() // 可用于配置路由
provideHttpClient() // 可用于配置 HttpClient 服务
provideZoneChangeDetection() // 可用于配置 ngZone
provideAnimations() // 需要动画时,使用该方法
provideNoopAnimations()
provideServiceWorker()
provideServerRendering()
provideClientHydration()

在之前 NgModule 应用中,会根据在 @ngModule.providers 或者 @({providedIn: “…”}) 中配置依赖,根据配置创建模块注入器,在独立应用中,没有模块的概念,Angular 会创建环境注入器 environment injectors 。
一下场景会创建环境注入器:
@NgModule.providers ,通过 NgModule 引导的应用程序时
@({provideIn: “…”})
bootstrapApplication 独立应用启动时配置的 providers
Route 配置的 providers

除了在以上使用中 Angular 创建环境注入器,Angular 还支持 createEnvironmentInjector 创建。
路由

Angular 为支持独立应用,提供了 provideRouter 方法用于配置独立应用的路由。

export const routes: Routes = [ { path: 'products', component: ProductListComponent }
];
bootstrapApplication(AppComponent, {providers: [provideRouter(routes),// importProvidersFrom(//   RouterModule.forRoot(routes)// ),]
}).catch((err) => console.error(err));

惰性加载

之前,要惰性加载模块,Angular 提供了 loadChildren 方法,加载一组路由,使用如下:

const routes: Routes = [{path: 'items',loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)}
];

在独立应用中,同样支持通过 loadChildren 惰性加载一组路由

// In the main application:
export const ROUTES: Route[] = [{path: 'admin', loadChildren: () => import('./admin/routes').then(mod => mod.ADMIN_ROUTES)},// ...
];
// In admin/routes.ts:
export const ADMIN_ROUTES: Route[] = [{path: 'home', component: AdminHomeComponent},{path: 'users', component: AdminUsersComponent},// ...
];

此外还支持 loadComponent 加载独立组件。

export const ROUTES: Route[] = [{path: 'admin', loadComponent: () => import('./admin/panel.component').then(mod => mod.AdminPanelComponent)},
];

简化使用:路由器会理解并使用 default 导出来的路由或者组件。

// In the main application:
export const ROUTES: Route[] = [{path: 'admin', loadChildren: () => import('./admin/routes')},// ...
];
// In admin/routes.ts:
export default [{path: 'home', component: AdminHomeComponent},{path: 'users', component: AdminUsersComponent},// ...
] as Route[];

工具

原有升级应用或类库升级使用独立组件
Angular (版本大于 15.2.0) 提供了完善的 Schematic 帮助原有应用升级转换为独立组件模式。

ng generate @angular/core:standalone

按照下面列出的顺序运行迁移:
运行 ng g @angular/core:standalone 并选择 “Convert all components, directives and pipes to standalone”
运行 ng g @angular/core:standalone 并选择 “Remove unnecessary NgModule classes”
运行 ng g @angular/core:standalone 并选择 “Bootstrap the project using standalone APIs”
运行任何静态分析(lint)和格式检查,修复任何故障,并提交结果

生成独立启动应用

ng new demo-standalone --standalone

生成独立组件

ng g c button --standalone

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com