组件生命周期、数据绑定、指令与管道、依赖注入
面试中,Angular 核心概念是考察候选人基础扎实度的关键区域。本章围绕组件生命周期、数据绑定、指令 & 管道、依赖注入四大模块,拆解高频面试题的回答框架与加分点。
| 概念 | 作用域 | 典型使用场景 | 常见陷阱 |
|---|---|---|---|
| 生命周期 | 组件实例 | 初始化数据、清理订阅 | 在 constructor 中调用 DOM |
| 数据绑定 | 模板 ↔ 组件 | 展示数据、响应事件 | 双向绑定直接修改对象引用 |
| 指令 & 管道 | 模板 | 结构复用、数据变换 | 纯管道不检测对象内部变化 |
| 依赖注入 | 模块/组件 | 服务复用、解耦 | provideIn 声明但忘记移除 |
🎯 考察点:对生命周期阶段的理解,以及实际开发中正确的初始化时机。
- 按顺序列举主钩子:
ngOnChanges → ngOnInit → ngDoCheck → ngAfterContentInit → ngAfterContentChecked → ngAfterViewInit → ngAfterViewChecked → ngOnDestroy - 指出
ngOnInit是最合适的初始化点(此时输入属性已绑定,可安全访问依赖) - 说明
constructor中应避免初始化逻辑(未完成依赖注入,无法访问 DOM)
💡 简短示例回答:生命周期顺序:ngOnChanges→ngOnInit→ngDoCheck→ngAfterContentInit→ngAfterContentChecked→ngAfterViewInit→ngAfterViewChecked→ngOnDestroy。最安全的数据初始化在 ngOnInit,因为此时输入属性已赋值,且服务已注入;constructor 只做简单赋值。
⚠️ 可能追问:“父子组件生命周期交替顺序是怎样的?” 答案:父组件构造函数→子组件完整生命周期→父组件 ngAfterViewInit。
ngAfterViewInit 中直接修改数据模型,会导致“表达式已变更”错误;若需要修改,用 setTimeout 或 ChangeDetectorRef.detectChanges()。
🎯 考察点:对模板与组件通信机制的掌握,以及单向/双向绑定的区别。
- 插值:
{{ expression }}→ 单向,将组件属性显示到模板 - 属性绑定:
[target]="expression"→ 单向,动态设置 DOM 属性 - 事件绑定:
(target)="handler()"→ 单向,模板→组件 - 双向绑定:
[(ngModel)]→ 结合属性+事件,同步视图与模型
💡 简短示例回答:四种绑定:插值 {{name}};属性 [src]="imgUrl";事件 (click)="onClick()";双向 [(ngModel)]="value"。双向绑定时 Angular 通过 @Input+@Output 模拟,实际是属性绑定和事件绑定的语法糖。
⚠️ 可能追问:“ngModel 如何实现双向绑定?” 答:通过 @Input() ngModel 和 @Output() ngModelChange 事件完成。
[(ngModel)] 时需要确保在模块中导入 FormsModule;避免直接修改引用类型对象(如数组)导致变更检测不生效,优先使用不可变操作。
🎯 考察点:指令分类和依赖注入配置的理解,以及纯管道与不纯管道的区别。
- 结构型:改变 DOM 结构(
*ngIf、*ngFor),通常用*语法糖 - 属性型:改变元素外观或行为(
ngClass、ngStyle) - provideIn:在
@Injectable中指定root或具体模块,实现树摇优化,只有被使用时才实例化服务 - 纯 vs 不纯管道:纯管道只在输入变化时执行(效率高),不纯管道在每次变更检测时执行
💡 简短示例回答:结构型指令如 *ngIf 控制元素创建/销毁;属性型如 ngClass 动态设置样式。依赖注入中 @Injectable({providedIn: 'root'}) 使服务成为单例且可被 tree-shake;纯管道默认纯,需手动设为 pure: false。
⚠️ 可能追问:“如何自定义一个结构型指令?” 答:使用 @Directive 注入 ViewContainerRef 和 TemplateRef,在 set 方法中处理创建/清除逻辑。
* 语法会被 Angular 展开为 <ng-template>;自定义管道如果修改了输入对象内部属性,纯管道无法检测,应改用不纯管道或返回新对象。
OnDestroy 的实际应用;讲解数据绑定时可提及 ChangeDetectionStrategy.OnPush 优化策略。