组件生命周期、数据绑定、指令与管道、依赖注入

面试中,Angular 核心概念是考察候选人基础扎实度的关键区域。本章围绕组件生命周期、数据绑定、指令 & 管道、依赖注入四大模块,拆解高频面试题的回答框架与加分点。

组件生命周期回答框架 生命周期总览 创建阶段 constructor → ngOnChanges 更新阶段 ngDoCheck → ngAfterContentChecked 展示阶段 ngAfterViewInit → ngAfterContentInit 销毁阶段 ngOnDestroy 清理 追问点:父子组件生命周期交替
📌 核心概念对比表
概念 作用域 典型使用场景 常见陷阱
生命周期 组件实例 初始化数据、清理订阅 在 constructor 中调用 DOM
数据绑定 模板 ↔ 组件 展示数据、响应事件 双向绑定直接修改对象引用
指令 & 管道 模板 结构复用、数据变换 纯管道不检测对象内部变化
依赖注入 模块/组件 服务复用、解耦 provideIn 声明但忘记移除
Q1 Angular 组件生命周期钩子的执行顺序是怎样的?在哪个钩子里初始化数据最安全?

🎯 考察点:对生命周期阶段的理解,以及实际开发中正确的初始化时机。

📝 回答框架:
  1. 按顺序列举主钩子:ngOnChanges → ngOnInit → ngDoCheck → ngAfterContentInit → ngAfterContentChecked → ngAfterViewInit → ngAfterViewChecked → ngOnDestroy
  2. 指出 ngOnInit 是最合适的初始化点(此时输入属性已绑定,可安全访问依赖)
  3. 说明 constructor 中应避免初始化逻辑(未完成依赖注入,无法访问 DOM)

💡 简短示例回答:生命周期顺序:ngOnChanges→ngOnInit→ngDoCheck→ngAfterContentInit→ngAfterContentChecked→ngAfterViewInit→ngAfterViewChecked→ngOnDestroy。最安全的数据初始化在 ngOnInit,因为此时输入属性已赋值,且服务已注入;constructor 只做简单赋值。

⚠️ 可能追问:“父子组件生命周期交替顺序是怎样的?” 答案:父组件构造函数→子组件完整生命周期→父组件 ngAfterViewInit。

🚨 避坑提醒:不要在 ngAfterViewInit 中直接修改数据模型,会导致“表达式已变更”错误;若需要修改,用 setTimeout 或 ChangeDetectorRef.detectChanges()。
Q2 解释 Angular 中的四种数据绑定方式,并说明它们的工作原理。

🎯 考察点:对模板与组件通信机制的掌握,以及单向/双向绑定的区别。

📝 回答框架:
  • 插值:{{ expression }} → 单向,将组件属性显示到模板
  • 属性绑定:[target]="expression" → 单向,动态设置 DOM 属性
  • 事件绑定:(target)="handler()" → 单向,模板→组件
  • 双向绑定:[(ngModel)] → 结合属性+事件,同步视图与模型

💡 简短示例回答:四种绑定:插值 {{name}};属性 [src]="imgUrl";事件 (click)="onClick()";双向 [(ngModel)]="value"。双向绑定时 Angular 通过 @Input+@Output 模拟,实际是属性绑定和事件绑定的语法糖。

⚠️ 可能追问:“ngModel 如何实现双向绑定?” 答:通过 @Input() ngModel 和 @Output() ngModelChange 事件完成。

🚨 避坑提醒:使用 [(ngModel)] 时需要确保在模块中导入 FormsModule;避免直接修改引用类型对象(如数组)导致变更检测不生效,优先使用不可变操作。
Q3 Angular 结构型指令和属性型指令有什么区别?依赖注入中的 provideIn 作用是什么?

🎯 考察点:指令分类和依赖注入配置的理解,以及纯管道与不纯管道的区别。

📝 回答框架:
  • 结构型:改变 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 优化策略。