ngOnChanges() 是 Angular 中的一个生命周期钩子函数,它在组件或指令的 输入属性 (@Input())发生变化时被调用。这个钩子可以帮助你检测输入数据的变化,并对数据变化做出反应。
1.监听的对象
 ngOnChanges() 监听的是 通过 @Input() 装饰器传递给子组件的属性 。换句话说,任何标记为 @Input() 的属性都会被 ngOnChanges() 监听。具体来说,它会监听通过父组件传递给子组件的属性变化。
2.触发时机
 ngOnChanges() 会在 输入属性的值变化时 被调用,且每次变化都会传递给钩子一个 SimpleChanges 对象,其中包含了该输入属性的前后值。
- 第一次调用 :在组件初始化时,如果有输入属性,
ngOnChanges()会在组件的构造函数和ngOnInit()之前被调用。 - 后续调用 :每次输入属性的值发生变化时,
ngOnChanges()会被再次调用。 
3.参数:SimpleChanges
 ngOnChanges() 的参数是一个 SimpleChanges 对象,它是一个字典对象,包含了所有发生变化的输入属性。每个输入属性的变化信息是一个 SimpleChange 对象,它包含以下属性:
- 
previousValue:变化前的值。 - 
currentValue:变化后的值。 - 
firstChange:一个布尔值,表示该输入属性是否是第一次变化。 - 
示例:
javascriptngOnChanges(changes: SimpleChanges): void { if (changes['inputProp']) { const previousValue = changes['inputProp'].previousValue; const currentValue = changes['inputProp'].currentValue; const isFirstChange = changes['inputProp'].firstChange; console.log(`inputProp changed from ${previousValue} to ${currentValue}`); console.log(`Is this the first change? ${isFirstChange}`); } } 
4.语法
            
            
              javascript
              
              
            
          
          ngOnChanges(changes: SimpleChanges): void {
  // 处理属性变化
}
        5.示例代码
假设有一个父组件 ParentComponent 和一个子组件 ChildComponent,父组件向子组件传递输入属性。
父组件:
            
            
              javascript
              
              
            
          
          @Component({
  selector: 'app-parent',
  template: `
    <app-child [inputProp]="parentData"></app-child>
    <button (click)="changeData()">Change Data</button>
  `
})
export class ParentComponent {
  parentData = 'Hello';
  changeData() {
    this.parentData = 'Hello, Angular!';
  }
}
        子组件:
            
            
              javascript
              
              
            
          
          @Component({
  selector: 'app-child',
  template: `<p>{{ inputProp }}</p>`
})
export class ChildComponent implements OnChanges {
  @Input() inputProp: string;
  ngOnChanges(changes: SimpleChanges) {
    if (changes['inputProp']) {
      const previousValue = changes['inputProp'].previousValue;
      const currentValue = changes['inputProp'].currentValue;
      const firstChange = changes['inputProp'].firstChange;
      console.log(`inputProp changed from ${previousValue} to ${currentValue}`);
      console.log(`Is this the first change? ${firstChange}`);
    }
  }
}
        6.解析:
- 当 
ParentComponent中的parentData发生变化时,ChildComponent中的inputProp将触发ngOnChanges()钩子。 SimpleChanges对象中将包含inputProp属性的变化信息,其中包括前一个值、当前值和是否为首次变化的信息。
7.使用场景
- 响应输入变化:当需要在输入数据发生变化时做一些额外处理,例如重新计算、重新加载数据等。
 - 优化性能:可以在数据变化时执行优化操作,例如避免不必要的 API 调用。
 - 数据监控 :在多个输入属性同时变化时,使用 
ngOnChanges()可以监控不同输入的变化并做出响应。 
8.注意事项
ngOnChanges()会在每次输入属性变化时被调用,因此如果输入属性频繁变化,ngOnChanges()也会频繁触发,可能影响性能。- 如果组件的输入属性没有变化,
ngOnChanges()不会被触发。 ngOnChanges()仅在@Input()属性变化时触发,不会监控其他类型的变化(例如,组件内部的局部状态变化)。
总结:
 ngOnChanges() 监听的是组件的 输入属性(@Input()) ,每当父组件传递给子组件的输入属性发生变化时,ngOnChanges() 会被触发并接收一个 SimpleChanges 对象,包含所有变化的输入属性的详细信息。
- 监听对象 :
@Input()属性。 - 变化内容:父组件传递给子组件的数据变化。
 
附加说明:为什么有个时候可以接收到 currentValue 和 previousValue 都是 undefined 的情况?这种情况不是应该没有变化嘛?为什么可以检测到?
在 Angular 中,如果你在 ngOnChanges() 中看到 currentValue 和 previousValue 都是 undefined,这可能是由于一些特定的场景或代码逻辑导致的。下面是可能的分析:
1. 组件初始化时的行为
ngOnChanges() 会在 组件初始化时触发一次,特别是在组件的输入属性第一次被设置时。
- 首次输入时 ,Angular 会触发一次 
ngOnChanges(),即使没有任何显式的变化。 - 在这个第一次触发时,
previousValue和currentValue可能会是undefined,因为此时 Angular 并没有从父组件传递任何值到子组件,或者在组件构造函数中初始化时,输入属性还没有赋值。 
举个例子:
            
            
              javascript
              
              
            
          
          @Component({
  selector: 'app-child',
  template: `<p>{{ inputProp }}</p>`
})
export class ChildComponent implements OnChanges {
  @Input() inputProp: string;
  ngOnChanges(changes: SimpleChanges) {
    // 如果 inputProp 是第一次变化,可能会看到 undefined
    console.log(changes['inputProp']);
  }
}
        父组件:
            
            
              javascript
              
              
            
          
          @Component({
  selector: 'app-parent',
  template: `<app-child [inputProp]="parentData"></app-child>`
})
export class ParentComponent {
  parentData = 'Initial value';
}
        执行过程:
- 在 
ChildComponent第一次加载时,ngOnChanges()会被触发。此时inputProp可能是undefined,因为它是第一次接收到父组件的数据。 ngOnChanges()会触发一次,即使父组件的值是首次设置,也会传递SimpleChanges对象,inputProp的previousValue和currentValue可能都是undefined,因为 Angular 是在首次赋值时检查的。firstChange会是true,表示这是第一次赋值。
2. 父组件传递的数据为 undefined
如果父组件传递给子组件的 @Input() 属性值是 undefined,并且这个值发生了变化,ngOnChanges() 也会触发。在这种情况下,如果传递的数据本身就是 undefined,那么 previousValue 和 currentValue 可能都为 undefined。
例如:
            
            
              javascript
              
              
            
          
          @Component({
  selector: 'app-parent',
  template: `<app-child [inputProp]="parentData"></app-child>`
})
export class ParentComponent {
  parentData = undefined; // 或者 null
}
        如果父组件的 inputProp 值初始化为 undefined,当子组件的 ngOnChanges() 被调用时,changes['inputProp'] 中的 previousValue 和 currentValue 可能都是 undefined,因为 inputProp 被初始化为 undefined,并且没有显式的初值。
3. 异步数据变化
如果输入属性的值是通过异步操作(比如从 API 获取数据)赋值的,ngOnChanges() 可能会在组件初始化时被触发,但值还没有更新。
- 在这种情况下,
ngOnChanges()会先被调用一次,此时输入属性的值可能还没有赋值或者为undefined。 - 当异步数据到达并更新输入属性时,
ngOnChanges()会再次被调用,且这时SimpleChanges会包含有效的值。 
4. 组件初始化与 ngOnChanges() 的触发时机
Angular 在组件初始化时会触发 ngOnChanges(),即使输入属性的初始值是 undefined 或者 null,也会触发一次钩子函数。这时:
previousValue可能是undefined,因为组件初始化时没有值。currentValue可能也是undefined,因为输入属性的初值可能是undefined或null。
总结
当你看到 ngOnChanges() 中的 currentValue 和 previousValue 都是 undefined 时,通常是以下几种情况之一:
- 组件初始化时 :当组件第一次被创建并接收到 
@Input()数据时,Angular 会触发ngOnChanges(),即使没有显式的变化,也会将previousValue和currentValue都设为undefined,尤其当输入属性还没有被赋值时。 - 父组件的输入值是 
undefined:如果父组件传递的@Input()值本身是undefined,那么currentValue和previousValue也会是undefined。 - 异步赋值 :如果输入属性的值是通过异步操作(如 HTTP 请求)传递的,可能会在初始化时出现 
undefined,而在值更新后触发另一次ngOnChanges()。 
再次强调:
ngOnChanges()不是仅在值变化时触发,而是在每次输入属性的值发生变化时都会被触发,包括从undefined到undefined(即初始绑定时)。换句话说,ngOnChanges()会在 输入属性的生命周期开始时触发,即使它的值没有变化,也会触发一次钩子函数。