Angular模块(NgModule)是组织应用的基本构建块。理解模块系统对于构建可维护、可扩展的大型应用至关重要。
// app.module.ts - 根模块
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { SharedModule } from './shared/shared.module';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
// 声明属于本模块的组件、指令、管道
declarations: [
AppComponent
],
// 导入其他模块提供的功能
imports: [
BrowserModule, // 必需:浏览器支持
CoreModule, // 核心模块(单例服务)
SharedModule, // 共享模块(通用组件)
AppRoutingModule // 根路由模块
],
// 提供服务(全局单例)
providers: [],
// 指定应用的主视图(根组件)
bootstrap: [AppComponent],
// 导出模块的功能(供其他模块使用)
exports: []
})
export class AppModule { }
用于存放应用级别的单例服务和一次性初始化的组件。
// core/core.module.ts
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthGuard } from './guards/auth.guard';
import { AuthInterceptor } from './interceptors/auth.interceptor';
import { ErrorInterceptor } from './interceptors/error.interceptor';
import { ApiService } from './services/api.service';
import { AuthService } from './services/auth.service';
import { LoadingService } from './services/loading.service';
import { HeaderComponent } from './components/header/header.component';
import { FooterComponent } from './components/footer/footer.component';
@NgModule({
declarations: [
HeaderComponent,
FooterComponent
],
imports: [
CommonModule,
HttpClientModule
],
exports: [
HeaderComponent,
FooterComponent
],
providers: [
AuthGuard,
ApiService,
AuthService,
LoadingService,
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true
}
]
})
export class CoreModule {
// 防止CoreModule被重复导入
constructor(@Optional() @SkipSelf() parentModule?: CoreModule) {
if (parentModule) {
throw new Error(
'CoreModule 已经导入。只能在 AppModule 中导入核心模块。'
);
}
}
}
用于存放应用中多个模块共享的组件、指令和管道。
// shared/shared.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
// 通用组件
import { ButtonComponent } from './components/button/button.component';
import { CardComponent } from './components/card/card.component';
import { ModalComponent } from './components/modal/modal.component';
import { SpinnerComponent } from './components/spinner/spinner.component';
import { PaginationComponent } from './components/pagination/pagination.component';
// 指令
import { HighlightDirective } from './directives/highlight.directive';
import { ClickOutsideDirective } from './directives/click-outside.directive';
// 管道
import { TruncatePipe } from './pipes/truncate.pipe';
import { CurrencyFormatPipe } from './pipes/currency-format.pipe';
import { SafeHtmlPipe } from './pipes/safe-html.pipe';
@NgModule({
declarations: [
// 组件
ButtonComponent,
CardComponent,
ModalComponent,
SpinnerComponent,
PaginationComponent,
// 指令
HighlightDirective,
ClickOutsideDirective,
// 管道
TruncatePipe,
CurrencyFormatPipe,
SafeHtmlPipe
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule
],
exports: [
// 重新导出Angular模块
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
// 导出共享组件
ButtonComponent,
CardComponent,
ModalComponent,
SpinnerComponent,
PaginationComponent,
// 导出共享指令
HighlightDirective,
ClickOutsideDirective,
// 导出共享管道
TruncatePipe,
CurrencyFormatPipe,
SafeHtmlPipe
]
})
export class SharedModule { }
按功能划分的独立模块,可以支持惰性加载。
// features/products/products.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../../shared/shared.module';
import { ProductsRoutingModule } from './products-routing.module';
import { ProductListComponent } from './product-list/product-list.component';
import { ProductDetailComponent } from './product-detail/product-detail.component';
import { ProductEditComponent } from './product-edit/product-edit.component';
import { ProductSearchComponent } from './product-search/product-search.component';
import { ProductService } from './services/product.service';
import { ProductResolver } from './resolvers/product.resolver';
import { ProductFilterPipe } from './pipes/product-filter.pipe';
@NgModule({
declarations: [
ProductListComponent,
ProductDetailComponent,
ProductEditComponent,
ProductSearchComponent,
ProductFilterPipe
],
imports: [
CommonModule,
SharedModule, // 导入共享模块(而不是CommonModule + FormsModule等)
ProductsRoutingModule // 特性路由模块
],
providers: [
ProductService,
ProductResolver
]
})
export class ProductsModule { }
// features/products/products-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProductListComponent } from './product-list/product-list.component';
import { ProductDetailComponent } from './product-detail/product-detail.component';
import { ProductEditComponent } from './product-edit/product-edit.component';
import { ProductResolver } from './resolvers/product.resolver';
const routes: Routes = [
{
path: '',
component: ProductListComponent
},
{
path: 'search',
component: ProductSearchComponent
},
{
path: ':id',
component: ProductDetailComponent,
resolve: {
product: ProductResolver
}
},
{
path: ':id/edit',
component: ProductEditComponent,
resolve: {
product: ProductResolver
}
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ProductsRoutingModule { }
按需加载模块,提升应用初始加载性能。
// app-routing.module.ts - 配置惰性加载
import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
import { AuthGuard } from './core/guards/auth.guard';
const routes: Routes = [
{
path: '',
redirectTo: '/dashboard',
pathMatch: 'full'
},
{
path: 'dashboard',
loadChildren: () => import('./features/dashboard/dashboard.module')
.then(m => m.DashboardModule),
canActivate: [AuthGuard]
},
{
path: 'products',
loadChildren: () => import('./features/products/products.module')
.then(m => m.ProductsModule),
canActivate: [AuthGuard],
data: { preload: true } // 预加载标志
},
{
path: 'orders',
loadChildren: () => import('./features/orders/orders.module')
.then(m => m.OrdersModule),
canActivate: [AuthGuard]
},
{
path: 'admin',
loadChildren: () => import('./features/admin/admin.module')
.then(m => m.AdminModule),
canActivate: [AuthGuard],
data: { roles: ['admin'] } // 角色权限
},
{
path: 'auth',
loadChildren: () => import('./features/auth/auth.module')
.then(m => m.AuthModule)
},
{
path: '**',
loadChildren: () => import('./features/not-found/not-found.module')
.then(m => m.NotFoundModule)
}
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules, // 预加载所有模块
enableTracing: false, // 开发时启用路由追踪
scrollPositionRestoration: 'enabled' // 恢复滚动位置
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
// core/strategies/selective-preloading.strategy.ts
import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SelectivePreloadingStrategy implements PreloadingStrategy {
preloadedModules: string[] = [];
preload(route: Route, load: () => Observable): Observable {
if (route.data && route.data['preload'] && route.path) {
// 添加预加载模块路径
this.preloadedModules.push(route.path);
return load();
} else {
return of(null);
}
}
}
// 在路由模块中使用
import { SelectivePreloadingStrategy } from './core/strategies/selective-preloading.strategy';
@NgModule({
imports: [RouterModule.forRoot(routes, {
preloadingStrategy: SelectivePreloadingStrategy
})],
providers: [SelectivePreloadingStrategy],
exports: [RouterModule]
})
export class AppRoutingModule { }
src/
├── app/
│ ├── core/ # 核心模块(单例服务,一次性组件)
│ │ ├── components/ # 核心组件(Header, Footer等)
│ │ ├── guards/ # 路由守卫
│ │ ├── interceptors/ # HTTP拦截器
│ │ ├── models/ # 核心模型/接口
│ │ ├── services/ # 单例服务
│ │ ├── strategies/ # 策略(预加载等)
│ │ ├── utils/ # 工具函数
│ │ └── core.module.ts
│ │
│ ├── shared/ # 共享模块(通用组件/指令/管道)
│ │ ├── components/ # 可复用组件
│ │ ├── directives/ # 共享指令
│ │ ├── pipes/ # 共享管道
│ │ └── shared.module.ts
│ │
│ ├── features/ # 特性模块(按功能划分)
│ │ ├── dashboard/ # 仪表板模块
│ │ │ ├── components/
│ │ │ ├── services/
│ │ │ └── dashboard.module.ts
│ │ │
│ │ ├── products/ # 产品管理模块
│ │ │ ├── components/
│ │ │ ├── services/
│ │ │ └── products.module.ts
│ │ │
│ │ └── auth/ # 认证模块
│ │ ├── components/
│ │ ├── services/
│ │ └── auth.module.ts
│ │
│ ├── app.component.ts # 根组件
│ ├── app.module.ts # 根模块
│ └── app-routing.module.ts # 根路由配置
│
├── assets/ # 静态资源
├── environments/ # 环境配置
└── styles/ # 全局样式
// 方案1:在根模块提供服务(单例)
// app.module.ts
@NgModule({
providers: [
UserService // 全局单例,所有模块共享
]
})
// 方案2:使用providedIn: 'root'
// user.service.ts
@Injectable({
providedIn: 'root' // 自动在根注入器中提供
})
export class UserService { }
// 方案3:模块级别的服务
// products.module.ts
@NgModule({
providers: [
ProductService // 只在ProductsModule作用域内
]
})
// 方案4:在CoreModule中提供核心服务
// core.module.ts
@NgModule({
providers: [
ApiService,
AuthService,
LoggingService
]
})
// ❌ 错误:循环导入
// module-a.ts 导入 module-b.ts,同时 module-b.ts 导入 module-a.ts
// ✅ 正确:使用共享模块
// shared.module.ts
@NgModule({
exports: [CommonModule, FormsModule, SomeSharedComponent]
})
// ✅ 正确:模块重构
// 将共享内容提取到第三个模块中
// 重新导出模块的模式
@NgModule({
imports: [CommonModule, FormsModule, SomeThirdPartyModule],
exports: [CommonModule, FormsModule, SomeThirdPartyModule]
})
export class ReexportModule { }
// 动态加载模块(非路由)
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
@Component({
selector: 'app-dynamic-loader',
template: `<ng-container #container></ng-container>`
})
export class DynamicLoaderComponent {
@ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
constructor(private cfr: ComponentFactoryResolver) {}
async loadModule() {
// 动态导入模块
const { FeatureModule } = await import('./features/feature/feature.module');
// 创建NgModule工厂
const moduleFactory = this.createNgModuleFactory(FeatureModule);
// 创建模块引用
const moduleRef = moduleFactory.create(this.injector);
// 获取模块中的组件工厂
const componentFactory = moduleRef.componentFactoryResolver
.resolveComponentFactory(FeatureComponent);
// 创建组件
this.container.clear();
this.container.createComponent(componentFactory);
}
private createNgModuleFactory(moduleType: any) {
// 使用编译器创建模块工厂
// 注:实际实现需要注入Compiler
}
}
forChild()而不是forRoot()创建特性模块路由providedIn: 'root'避免重复服务实例core.module.ts的构造函数防止重复导入// 服务摇树优化
// 仅在需要时提供特定服务
@NgModule({
providers: [
{
provide: 'API_CONFIG',
useFactory: () => {
// 动态配置,支持摇树
return environment.apiConfig;
}
}
]
})
// 条件性导入模块
const imports = [
CommonModule,
SharedModule
];
// 仅在开发环境导入调试模块
if (!environment.production) {
imports.push(DevToolsModule);
}
@NgModule({
imports: imports
})
// 为测试创建专用模块
// test.module.ts
import { TestBed } from '@angular/core/testing';
export function createTestModule(metadata: NgModule): void {
@NgModule({
imports: metadata.imports || [],
declarations: metadata.declarations || [],
providers: metadata.providers || [],
exports: metadata.exports || []
})
class TestModule {}
TestBed.configureTestingModule({
imports: [TestModule]
});
}
// 测试中使用
beforeEach(() => {
createTestModule({
imports: [SharedModule],
declarations: [TestComponent],
providers: [TestService]
});
});
// 特性模块的测试配置
// products.module.spec.ts
describe('ProductsModule', () => {
let module: ProductsModule;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProductsModule]
});
module = TestBed.inject(ProductsModule);
});
it('应该创建模块', () => {
expect(module).toBeTruthy();
});
});
// 使用独立组件API(Angular 14+)
// product-list.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProductCardComponent } from '../product-card/product-card.component';
@Component({
selector: 'app-product-list',
standalone: true, // 独立组件
imports: [CommonModule, ProductCardComponent], // 直接导入依赖
template: `
<div *ngFor="let product of products">
<app-product-card [product]="product"></app-product-card>
</div>
`
})
export class ProductListComponent {
products = [...];
}
// 惰性加载独立组件
const routes: Routes = [{
path: 'products',
loadComponent: () => import('./product-list.component')
.then(m => m.ProductListComponent)
}];
// 库模块设计
// ui-library.module.ts
import { ModuleWithProviders, NgModule } from '@angular/core';
@NgModule({
declarations: [...],
exports: [...]
})
export class UiLibraryModule {
// 提供配置的静态方法
static forRoot(config: UiConfig): ModuleWithProviders<UiLibraryModule> {
return {
ngModule: UiLibraryModule,
providers: [
{
provide: UI_CONFIG,
useValue: config
}
]
};
}
// 不带配置的导入
static forChild(): ModuleWithProviders<UiLibraryModule> {
return {
ngModule: UiLibraryModule,
providers: []
};
}
}
// 使用
@NgModule({
imports: [
UiLibraryModule.forRoot({
theme: 'dark',
animations: true
})
]
})
export class AppModule { }
@NgModule({
imports: [
UiLibraryModule.forChild()
]
})
export class FeatureModule { }