第4章 组件通信与生命周期实战:@Input/@Output、服务通信、生命周期钩子应用
面试问题 1
问题:请详细描述 Angular 中 @Input 和 @Output 的实现原理,以及它们如何与变化检测配合工作?
考察点:是否理解数据绑定机制、变化检测触发时机、@Output 的 EventEmitter 本质。
回答框架:先讲 @Input 是属性绑定,Angular 在 ngOnChanges 中检测变化;再讲 @Output 是自定义事件,通过 EventEmitter 发射;最后强调变化检测默认走 zone.js。
示例回答:@Input 通过装饰器将父组件属性绑定到子组件,Angular 在每次变化检测循环中比较旧值,触发 ngOnChanges。@Output 利用 EventEmitter 发射自定义事件,父组件通过模板 () 语法订阅。变化检测由 zone.js 自动触发,也可手动 markForCheck。
可能追问:OnPush 策略下 @Input 变化如何检测?EventEmitter 为什么是 Observable?
面试问题 2
问题:跨层级组件(如祖孙组件)通信,如何使用 Service + Subject 实现?相比 @Input/@Output 有什么优势和代价?
考察点:依赖注入层级、Subject 热/冷特性、组件耦合度与可测试性。
回答框架:先指出需要在共享服务中定义 Subject,然后组件通过注入服务订阅/推送;优势在于解耦、跨层级;代价是必须手动管理订阅生命周期。
示例回答:在共享服务中创建 private subject,暴露 asObservable() 和 next() 方法。父组件注入并调用 next,子组件订阅。优势是无需逐层传递,可测试性强。代价是需在 ngOnDestory 中 unsubscribe,否则内存泄漏。
可能追问:Service 在模块提供 vs 组件提供有什么区别?BehaviorSubject 和 ReplaySubject 选哪个?
面试问题 3
问题:Angular 生命周期钩子的执行顺序是怎样的?如何利用 ngOnChanges 和 ngDoCheck 做性能优化?
考察点:钩子链调用时机、变化检测循环、脏检查与 Immutable 数据。
回答框架:先列顺序(ngOnChanges→ngOnInit→ngDoCheck→ngAfterContentInit→ngAfterContentChecked→ngAfterViewInit→ngAfterViewChecked→ngOnDestory),然后讲 ngOnChanges 只响应 @Input 变化,ngDoCheck 每次变化检测都触发,适合手动检查复杂对象。
示例回答:顺序牢记:输入变化检测→初始化→内容初始化→视图初始化→销毁。优化时:ngOnChanges 用简单比较减少计算;ngDoCheck 中避免繁重操作,如果是 OnPush 策略,用 ChangeDetectorRef.markForCheck 精确触发。
可能追问:ngAfterViewInit 和 ngAfterContentInit 的区别?什么时候用 ngAfterViewChecked?
项目案例:企业级动态表单引擎
| 项目名称 | 动态表单渲染系统(内部代号 FormFlow) |
| 业务背景 | 后台管理需要根据JSON配置动态生成复杂表单,涉及几十个组件间联动(下拉选单、联动筛选、数据校验)。通信关系复杂,早期使用 @Input/@Output 导致模板膨胀、跨层级传递困难。 |
| 规模指标 | 200+ 表单组件、30+ 业务模块、峰值 5 万并发表单提交、首次渲染 < 500ms |
| 技术栈 | Angular 14 + TypeScript + RxJS + Ngrx + OnPush 变化检测 |
| 核心难点 | 1. 祖孙组件间数据同步需避免过度冒泡;2. 表单状态响应式更新时需防止无效变化检测;3. 内存泄漏风险高,尤其复杂级联订阅。 |
| 面试高频追问 | • 为什么不用 @Input/@Output 实现祖孙通信? • Subject 和 BehaviorSubject 在项目中如何选型? • 如何通过生命周期钩子来优化渲染性能? • 如何对服务通信编写单元测试? |
| 可迁移回答话术 | “在项目中我们遇到了跨层级组件通信的痛点,采用共享服务 + BehaviorSubject 方案,将数据流统一管理。同时在 ngDestory 中通过 takeUntil 统一取消订阅,避免了内存泄漏。性能方面结合 OnPush 策略和 ChangeDetectorRef.markForCheck 精确控制变化检测,最终首屏渲染时间降低了 30%。这个经验可以用在任何需要解耦组件通信的场景中。” |