1.前言
首先简单认识下angular中组件和ngModule。
组件由一个TypeScript类、一个HTML模板和一个CSS样式表组成。TypeScript类定义了
HTML模板和渲染的DOM结构的交互,而样式表描述了它的外观。组件负责暴露数据,并通过数据绑定机制来处理绝大多数视图的显示和用户交互逻辑。
NgModule为一个组件集声明了编译的上下文环境。NgModule可以将组件和一组相关代码(如服务)关联起来,形成功能单元。每个应用都有一个根ngModule,可能有很多特性ngModel。
2.独立组件
我们直到现在都必须创建或者更新现有的ngModule才能使用组件,但是新的独立API可以直接编写组件、指令、管道而无需创建关联的ngModule。独立组件不属于任何ngModule的一部分,可以与其他独立组件或基于ngModule的组件一起使用。独立组件是自包含的,直接管理它们的模板依赖关系,ngModule变成可选的,重点从ngModule转换到了组件。
Angular 14发布了独立组件作为开发者预览,API是完整的,可以试验和探索,还没有稳定,angular 15之后独立组件稳定可用于生产环境,15.2.0之后angular提供给一个原理图ng generate @angular/core:standalone可以将现有项目迁移转换到独立组件,但还需要手动修复(删除多余的ngModule,代码格式化,其他引入等问题)。现有的应用可以选择性的以增量方式采用新的独立风格,无需任何重大更改。
2.1 创建独立组件
用cli工具(cli版本16以上)时使用--standalone标志创建独立组件、管道或指令。
ng g p pie --standalone
ng g d directive --standalone
ng g c component --standalone
正常创建组件:(会将组件添加到最近的ngModule的声明列表中)
创建独立组件:ng g c test --standalone
2.2 改造组件为独立组件
@Component({
selector: 'app-application-include',
templateUrl: './application-include.component.html',
styleUrls: ['./application-include.component.scss']
})
@Component({
standalone: true,
imports: [SharedModule, BusinessSelectComponent],
selector: 'app-application-include',
templateUrl: './application-include.component.html',
styleUrls: ['./application-include.component.scss']
})
- 在组件的类装饰器中添加standalone字段,将独立属性设置为true
- 在声明该组件的ngModule的声明列表中删除该组件
- 在组件的类装饰器中添加imports字段,添加该组件的依赖项
2.3 独立组件中的依赖关系
独立组件可能依赖于其他成员、管道和指令,这些依赖分为两类:
-
其他独立组件
-
其他模块中的组件
两种类型的依赖相关都使用imports装饰器数组添加到独立组件中。
@Component({
standalone: true,
imports: [SharedModule, BusinessSelectComponent],
selector: 'app-application-include',
templateUrl: './application-include.component.html',
styleUrls: ['./application-include.component.scss']
})
其中,SharedModule是依赖的组件、管道或指令的模块,BusinessSelectComponent独立组件。
2.4 独立组件的使用
独立组件的使用方式有两种:
-
另一个独立组件内
-
ngModule内
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ApplicationIncludeComponent } from 'src/app/modules/sharing/application-include'; @NgModule({ declarations: [ ], imports: [ CommonModule, ApplicationIncludeComponent, ] }) export class ApplicationManageModule { }
在ngModule中使用时,不能放到声明列表中,必须用导入的方式引入。
使用前向类解决循环依赖
在TypeScript中,类声明的顺序是很重要的。如果一个类尚未定义,就不能引用它。这通常不是问题,但有时循环引用是不可避免的。例如,当类“A”引用类“B”而“B”也引用“A”时。其中之一必须首先定义。
Angular的forwardRef()函数创建了一个Angular稍后可以解析的间接引用。
例如,当独立父组件导入独立子组件时可能会发生这种情况,反之亦然。可以使用forwardRef函数解决此循环依赖问题。
@Component({
standalone: true,
imports: [ChildComponent],
selector: 'app-parent',
template: `<app-child [hideParent]="hideParent"></app-child>`,
})
export class ParentComponent {
hideParent = true;
}
@Component({
standalone: true,
imports: [CommonModule, forwardRef(() => ParentComponent)],
selector: 'app-child',
template: `<app-parent *ngIf="!hideParent"></app-parent>`,
})
export class ChildComponent {
@Input() hideParent: boolean;
}
需要特别注意的是这种类型的导入可能会导致组件实例化期间出现无限递归。一定要
确保这种递归具有停止条件,会在某种时刻停止。(如组件的渲染添加判断条件)
独立组件引导应用程序
使用独立组件作为应用程序的根组件,在没有任何NgModule的情况下引导Angular
应用程序。使用bootstrapApplication API来完成的:
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, { providers: []
});
独立组件配置路由
任何路由都可以用loadComponent懒加载路由到的独立组件,只要加载的组件是独立组件,就可以用懒加载的方式。
export const ROUTES: Route[] = [
{path: 'admin', loadComponent: () => import('./admin/panel.component').then(m => m.AdminPanelComponent)},
];
Angular库中的应用
@Component({
selector: 'app-sticky-table',
standalone: true,
imports: [
CommonModule,
FormsModule,
],
templateUrl: './sticky-table.component.html',
styleUrls: ['./sticky-table.component.scss']
})
直接用独立组件导出即可,简化代码。
推荐使用独立组件的方式,这种模式更有利于业务侧代码按需引入组件。
2.5 独立组件的优点
- 简化组件写法:ngModule可选,编写代码更少,需要修改的文件更少
- 学习更简单,理解的概念更少
- 懒加载模式更简单:独立组件使得打包、重用、懒加载都更简单
2.6 独立组件的可用性
- Ng14及其之后版本可用
- 与现有应用程序完全兼容
- 无重大变更,无需任何重写