面试题答案
一键面试模块组织架构层面优化以减少初始加载时间和内存占用
- 合理划分模块
- 按功能模块划分:将应用按照不同的业务功能,如用户管理、订单管理、报表展示等,拆分成独立的模块。每个模块负责单一的功能领域,这样在初始加载时,只加载核心功能模块,其他功能模块可以按需懒加载。
- 粒度控制:避免模块过于庞大,对于一些较大的功能模块,可以进一步细分。例如,在用户管理模块中,如果包含复杂的权限管理功能,可以将权限管理部分单独拆分成一个子模块进行懒加载。
- 使用延迟加载策略
- 在Angular中,通过
loadChildren
属性实现模块的懒加载。例如,在app - routing.module.ts
文件中配置路由时:
- 在Angular中,通过
const routes: Routes = [
{
path: 'user - management',
loadChildren: () => import('./user - management/user - management.module').then(m => m.UserManagementModule)
},
{
path: 'order - management',
loadChildren: () => import('./order - management/order - management.module').then(m => m.OrderManagementModule)
}
];
这样在应用启动时,user - management
和order - management
模块不会被立即加载,只有当用户访问对应的路由时才会加载。
- 预加载策略
- 虽然懒加载能减少初始加载时间,但对于一些预计用户很快会访问的模块,可以使用预加载策略。Angular提供了
PreloadAllModules
和自定义预加载策略。 - 使用
PreloadAllModules
:在app - module.ts
的RouterModule.forRoot
中配置:
- 虽然懒加载能减少初始加载时间,但对于一些预计用户很快会访问的模块,可以使用预加载策略。Angular提供了
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
- 自定义预加载策略:创建一个实现`PreloadingStrategy`接口的类,根据业务需求(如根据用户角色、历史访问记录等)决定哪些模块需要预加载。
保证懒加载模块之间以及与主模块之间通信高效且稳定
- 服务共享
- 单例服务:创建单例服务来管理共享数据和逻辑。在Angular中,当服务在根模块(
app - module.ts
)中提供时,它是一个单例服务,所有模块(包括懒加载模块)都可以使用同一个实例。例如,创建一个DataService
来管理应用中的一些全局数据:
- 单例服务:创建单例服务来管理共享数据和逻辑。在Angular中,当服务在根模块(
@Injectable({
providedIn: 'root'
})
export class DataService {
private sharedData: any;
getSharedData() {
return this.sharedData;
}
setSharedData(data: any) {
this.sharedData = data;
}
}
- **模块内服务**:如果懒加载模块有自己独有的共享数据需求,可以在模块内提供服务。在模块的`@NgModule`中配置`providers`数组,这样该模块及其子组件都可以使用这个服务实例,并且与其他模块的同名服务实例隔离。
2. 事件总线模式 - 创建一个事件总线服务,用于各模块之间的事件通信。例如:
@Injectable({
providedIn: 'root'
})
export class EventBusService {
private eventSubject = new Subject<any>();
event$ = this.eventSubject.asObservable();
publish(event: any) {
this.eventSubject.next(event);
}
}
- 在需要发送事件的组件(可能在主模块或懒加载模块中)注入`EventBusService`并调用`publish`方法:
@Component({... })
export class SomeComponent {
constructor(private eventBus: EventBusService) {
const eventData = { message: 'Some event occurred' };
this.eventBus.publish(eventData);
}
}
- 在需要监听事件的组件(同样可能在不同模块中)注入`EventBusService`并订阅`event$`:
@Component({... })
export class AnotherComponent {
constructor(private eventBus: EventBusService) {
this.eventBus.event$.subscribe((data) => {
console.log('Received event:', data);
});
}
}
- 输入输出属性和视图封装
- 父 - 子组件通信:当懒加载模块中的组件与主模块中的组件存在父子关系时,可以通过输入(
@Input()
)和输出(@Output()
)属性进行通信。例如,主模块中的父组件通过@Input()
传递数据给懒加载模块中的子组件:
- 父 - 子组件通信:当懒加载模块中的组件与主模块中的组件存在父子关系时,可以通过输入(
<!-- 主模块中的父组件模板 -->
<app - lazy - loaded - component [inputData]="parentData"></app - lazy - loaded - component>
// 懒加载模块中的子组件
@Component({... })
export class LazyLoadedComponent {
@Input() inputData: any;
}
- **视图封装**:合理使用视图封装策略(`ViewEncapsulation.None`、`ViewEncapsulation.Emulated`、`ViewEncapsulation.ShadowDom`),确保样式和组件之间的隔离与通信正常进行。一般情况下,`ViewEncapsulation.Emulated`能满足大多数需求,但在某些需要全局样式影响或特殊样式隔离场景下,需要调整封装策略。