组件生命周期 #

一、生命周期概述 #

Angular组件从创建到销毁会经历一系列阶段,每个阶段都有对应的钩子函数。

text
┌─────────────────────────────────────────────────────────────┐
│                    组件生命周期                              │
├─────────────────────────────────────────────────────────────┤
│  创建阶段                                                    │
│  ├── constructor()       构造函数                           │
│  ├── ngOnChanges()       输入属性变化                        │
│  └── ngOnInit()          初始化                             │
│                                                             │
│  更新阶段                                                    │
│  ├── ngDoCheck()         变更检测                           │
│  ├── ngOnChanges()       输入属性变化                        │
│  └── ngAfterContentChecked() 内容变更检测                    │
│                                                             │
│  视图阶段                                                    │
│  ├── ngAfterContentInit()    内容初始化                      │
│  ├── ngAfterViewInit()       视图初始化                      │
│  └── ngAfterViewChecked()    视图变更检测                    │
│                                                             │
│  销毁阶段                                                    │
│  └── ngOnDestroy()       组件销毁                           │
└─────────────────────────────────────────────────────────────┘

二、生命周期执行顺序 #

text
constructor()
    ↓
ngOnChanges()          (如果有@Input属性)
    ↓
ngOnInit()
    ↓
ngDoCheck()
    ↓
ngAfterContentInit()
    ↓
ngAfterContentChecked()
    ↓
ngAfterViewInit()
    ↓
ngAfterViewChecked()
    ↓
(变更检测循环)
    ↓
ngOnDestroy()

三、constructor构造函数 #

3.1 基本用法 #

typescript
import { Component } from '@angular/core';

@Component({
  selector: 'app-example',
  template: '<p>Example</p>'
})
export class ExampleComponent {
  name: string;
  
  constructor() {
    this.name = 'Angular';
    console.log('constructor执行');
  }
}

3.2 依赖注入 #

typescript
@Component({
  selector: 'app-user',
  template: '<p>{{ user.name }}</p>'
})
export class UserComponent {
  user: User;
  
  constructor(
    private userService: UserService,
    private route: ActivatedRoute
  ) {
    console.log('constructor: 依赖注入完成');
  }
}

3.3 constructor vs ngOnInit #

constructor ngOnInit
TypeScript特性 Angular特性
用于依赖注入 用于初始化逻辑
输入属性未绑定 输入属性已绑定
不应包含复杂逻辑 可包含初始化逻辑

四、ngOnChanges #

4.1 基本用法 #

typescript
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-child',
  template: '<p>{{ name }}</p>'
})
export class ChildComponent implements OnChanges {
  @Input() name: string = '';
  @Input() age: number = 0;
  
  ngOnChanges(changes: SimpleChanges) {
    console.log('ngOnChanges:', changes);
    
    if (changes['name']) {
      const prevName = changes['name'].previousValue;
      const currentName = changes['name'].currentValue;
      const isFirstChange = changes['name'].firstChange;
      
      console.log(`name: ${prevName} → ${currentName}`);
    }
    
    if (changes['age']) {
      console.log(`age: ${changes['age'].previousValue} → ${changes['age'].currentValue}`);
    }
  }
}

4.2 SimpleChanges对象 #

typescript
interface SimpleChange {
  previousValue: any;    // 之前的值
  currentValue: any;     // 当前的值
  firstChange: boolean;  // 是否首次变化
}

// changes对象结构
{
  name: {
    previousValue: 'John',
    currentValue: 'Jane',
    firstChange: false
  },
  age: {
    previousValue: 25,
    currentValue: 26,
    firstChange: false
  }
}

4.3 使用场景 #

typescript
@Component({
  selector: 'app-user-detail',
  template: `
    <div *ngIf="user">
      <h3>{{ user.name }}</h3>
      <p>{{ formattedInfo }}</p>
    </div>
  `
})
export class UserDetailComponent implements OnChanges {
  @Input() user: User;
  formattedInfo: string;
  
  ngOnChanges(changes: SimpleChanges) {
    if (changes['user'] && this.user) {
      this.formattedInfo = this.formatUserInfo(this.user);
    }
  }
  
  private formatUserInfo(user: User): string {
    return `${user.name}, ${user.age}岁, ${user.email}`;
  }
}

五、ngOnInit #

5.1 基本用法 #

typescript
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-user',
  template: '<p>{{ user.name }}</p>'
})
export class UserComponent implements OnInit {
  user: User;
  
  constructor(private userService: UserService) {}
  
  ngOnInit() {
    this.loadUser();
  }
  
  private loadUser() {
    this.userService.getUser().subscribe(user => {
      this.user = user;
    });
  }
}

5.2 初始化逻辑 #

typescript
@Component({
  selector: 'app-product',
  template: `
    <div *ngFor="let product of products">
      {{ product.name }}
    </div>
  `
})
export class ProductComponent implements OnInit {
  products: Product[] = [];
  
  constructor(
    private productService: ProductService,
    private route: ActivatedRoute
  ) {}
  
  ngOnInit() {
    this.route.params.subscribe(params => {
      const categoryId = params['id'];
      this.loadProducts(categoryId);
    });
  }
  
  private loadProducts(categoryId: number) {
    this.productService.getProducts(categoryId)
      .subscribe(products => {
        this.products = products;
      });
  }
}

5.3 ngOnInit最佳实践 #

typescript
@Component({
  selector: 'app-example',
  template: '...'
})
export class ExampleComponent implements OnInit {
  private destroy$ = new Subject<void>();
  
  constructor(private service: DataService) {}
  
  ngOnInit() {
    this.service.getData()
      .pipe(takeUntil(this.destroy$))
      .subscribe(data => {
        console.log(data);
      });
  }
  
  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

六、ngDoCheck #

6.1 基本用法 #

typescript
import { Component, DoCheck } from '@angular/core';

@Component({
  selector: 'app-example',
  template: '<p>{{ message }}</p>'
})
export class ExampleComponent implements DoCheck {
  message = 'Hello';
  
  ngDoCheck() {
    console.log('ngDoCheck: 变更检测执行');
  }
}

6.2 自定义变更检测 #

typescript
@Component({
  selector: 'app-list',
  template: `
    <p>列表长度: {{ items.length }}</p>
    <p>总价格: {{ totalPrice }}</p>
  `
})
export class ListComponent implements DoCheck {
  @Input() items: Item[] = [];
  totalPrice: number = 0;
  private previousLength: number = 0;
  
  ngDoCheck() {
    if (this.items.length !== this.previousLength) {
      this.previousLength = this.items.length;
      this.calculateTotal();
    }
  }
  
  private calculateTotal() {
    this.totalPrice = this.items.reduce((sum, item) => sum + item.price, 0);
  }
}

七、内容投影钩子 #

7.1 ngAfterContentInit #

typescript
import { Component, ContentChild, AfterContentInit } from '@angular/core';

@Component({
  selector: 'app-wrapper',
  template: `
    <div class="wrapper">
      <ng-content></ng-content>
    </div>
  `
})
export class WrapperComponent implements AfterContentInit {
  @ContentChild('header') header;
  
  ngAfterContentInit() {
    console.log('内容投影初始化完成');
    console.log('header:', this.header);
  }
}

7.2 ngAfterContentChecked #

typescript
@Component({
  selector: 'app-wrapper',
  template: `
    <div class="wrapper">
      <ng-content></ng-content>
    </div>
  `
})
export class WrapperComponent implements AfterContentChecked {
  ngAfterContentChecked() {
    console.log('内容变更检测完成');
  }
}

八、视图钩子 #

8.1 ngAfterViewInit #

typescript
import { Component, ViewChild, AfterViewInit, ElementRef } from '@angular/core';

@Component({
  selector: 'app-example',
  template: `
    <input #input type="text" />
    <app-child #child></app-child>
  `
})
export class ExampleComponent implements AfterViewInit {
  @ViewChild('input') inputElement: ElementRef;
  @ViewChild('child') childComponent: ChildComponent;
  
  ngAfterViewInit() {
    console.log('视图初始化完成');
    
    this.inputElement.nativeElement.focus();
    
    console.log('子组件:', this.childComponent);
  }
}

8.2 ngAfterViewChecked #

typescript
@Component({
  selector: 'app-example',
  template: `
    <p>{{ message }}</p>
    <app-child></app-child>
  `
})
export class ExampleComponent implements AfterViewChecked {
  message = 'Hello';
  
  ngAfterViewChecked() {
    console.log('视图变更检测完成');
  }
}

8.3 视图更新注意事项 #

typescript
@Component({
  selector: 'app-example',
  template: '<p>{{ message }}</p>'
})
export class ExampleComponent implements AfterViewChecked {
  message = 'Hello';
  
  ngAfterViewChecked() {
    // 错误:会导致无限循环
    // this.message = 'Updated';
    
    // 正确:使用setTimeout
    // setTimeout(() => {
    //   this.message = 'Updated';
    // });
  }
}

九、ngOnDestroy #

9.1 基本用法 #

typescript
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-example',
  template: '<p>Example</p>'
})
export class ExampleComponent implements OnDestroy {
  private subscription: Subscription;
  
  constructor(private service: DataService) {
    this.subscription = this.service.getData().subscribe();
  }
  
  ngOnDestroy() {
    console.log('组件销毁');
    this.subscription.unsubscribe();
  }
}

9.2 清理资源 #

typescript
@Component({
  selector: 'app-timer',
  template: '<p>{{ count }}</p>'
})
export class TimerComponent implements OnInit, OnDestroy {
  count = 0;
  private timer: any;
  
  ngOnInit() {
    this.timer = setInterval(() => {
      this.count++;
    }, 1000);
  }
  
  ngOnDestroy() {
    if (this.timer) {
      clearInterval(this.timer);
    }
  }
}

9.3 使用takeUntil模式 #

typescript
import { Component, OnDestroy } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';

@Component({
  selector: 'app-example',
  template: '...'
})
export class ExampleComponent implements OnDestroy {
  private destroy$ = new Subject<void>();
  
  constructor(
    private userService: UserService,
    private dataService: DataService
  ) {
    this.userService.getUser()
      .pipe(takeUntil(this.destroy$))
      .subscribe(user => {
        console.log(user);
      });
    
    this.dataService.getData()
      .pipe(takeUntil(this.destroy$))
      .subscribe(data => {
        console.log(data);
      });
  }
  
  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

十、生命周期完整示例 #

typescript
import { 
  Component, 
  Input, 
  OnInit, 
  OnChanges, 
  DoCheck, 
  AfterContentInit,
  AfterContentChecked,
  AfterViewInit,
  AfterViewChecked,
  OnDestroy,
  SimpleChanges
} from '@angular/core';

@Component({
  selector: 'app-lifecycle',
  template: `
    <div class="lifecycle">
      <h3>Lifecycle Demo</h3>
      <p>计数: {{ count }}</p>
      <button (click)="increment()">+1</button>
    </div>
  `
})
export class LifecycleComponent implements 
  OnInit, 
  OnChanges, 
  DoCheck,
  AfterContentInit,
  AfterContentChecked,
  AfterViewInit,
  AfterViewChecked,
  OnDestroy {
  
  @Input() initialCount: number = 0;
  count: number = 0;
  
  constructor() {
    console.log('1. constructor');
  }
  
  ngOnChanges(changes: SimpleChanges) {
    console.log('2. ngOnChanges', changes);
    if (changes['initialCount']) {
      this.count = this.initialCount;
    }
  }
  
  ngOnInit() {
    console.log('3. ngOnInit');
  }
  
  ngDoCheck() {
    console.log('4. ngDoCheck');
  }
  
  ngAfterContentInit() {
    console.log('5. ngAfterContentInit');
  }
  
  ngAfterContentChecked() {
    console.log('6. ngAfterContentChecked');
  }
  
  ngAfterViewInit() {
    console.log('7. ngAfterViewInit');
  }
  
  ngAfterViewChecked() {
    console.log('8. ngAfterViewChecked');
  }
  
  ngOnDestroy() {
    console.log('9. ngOnDestroy');
  }
  
  increment() {
    this.count++;
  }
}

十一、生命周期使用场景 #

钩子 使用场景
constructor 依赖注入、简单初始化
ngOnChanges 响应输入属性变化
ngOnInit 复杂初始化、获取数据
ngDoCheck 自定义变更检测
ngAfterContentInit 访问投影内容
ngAfterViewInit 访问视图、DOM操作
ngOnDestroy 清理资源、取消订阅

十二、总结 #

钩子 执行次数 说明
constructor 1次 构造函数
ngOnChanges 多次 输入属性变化时
ngOnInit 1次 初始化
ngDoCheck 多次 每次变更检测
ngAfterContentInit 1次 内容初始化
ngAfterContentChecked 多次 内容变更检测
ngAfterViewInit 1次 视图初始化
ngAfterViewChecked 多次 视图变更检测
ngOnDestroy 1次 组件销毁

下一步:内置指令

最后更新:2026-03-26