前言
在 Angular 中, 每個 Component 和 Directive 都有生命週期, 由一系列的生命週期 Hook 所組成. 理解這些生命週期鉤子函數, 可以讓你在適當的時機執行特定的邏輯.
LifeCycle Hook Function
LifeCycle Hook Function 讓開發者在 Component 和 Directive 的不同階段進行操作.
Angular LifeCycle Hook
constructor: ES6 提供給 class 的建構子, 並不屬於 AngularngOnChanges: 當 Component 的一個輸入屬性發生變化時調用ngOnInit: 當 Component 初始化完成時調用, 此時 Component 的輸入屬性和輸出屬性都已經確定ngDoCheck: 當 Angular 檢查 Component 的變化時調用, 通常是由於輸入屬性發生變化或因為事件觸發引起的ngAfterContentInit: 當 Component 的 Content 完成初始化之後調用, 通常用於對 Component 的 Content 執行操作ngAfterContentChecked: 當 Angular 檢查 Component 的 Content 發生變化時調用ngAfterViewInit: 當 Component 的 View 完成初始化之後調用, 通常用於對 Component 的 View 執行操作ngAfterViewChecked: 當 Angular 檢查 Component 的 View 發生變化時調用ngOnDestroy: 當 Component 被摧毀時調用, 通常用於釋放 Component 所占用的資源
生命週期執行順序
完整的生命週期執行順序如下:
- constructor: 建立 Component 實例
- ngOnChanges: 輸入屬性變更時 (首次也會執行)
- ngOnInit: Component 初始化完成
- ngDoCheck: 變更檢測
- ngAfterContentInit: 外部內容初始化完成
- ngAfterContentChecked: 外部內容變更檢測
- ngAfterViewInit: 視圖初始化完成
- ngAfterViewChecked: 視圖變更檢測
- ngOnDestroy: Component 銷毀前
常用生命週期範例
ngOnInit
最常使用的生命週期 Hook, 通常用於初始化資料.
export class UserComponent implements OnInit {
users: User[] = [];
constructor(private userService: UserService) {}
ngOnInit(): void {
// 在這裡初始化資料
this.loadUsers();
}
loadUsers(): void {
this.userService.getUsers().subscribe((users) => (this.users = users));
}
}
ngOnChanges
當 @Input() 屬性值改變時觸發.
export class ChildComponent implements OnChanges {
@Input() data: string;
ngOnChanges(changes: SimpleChanges): void {
console.log("Previous value:", changes["data"].previousValue);
console.log("Current value:", changes["data"].currentValue);
}
}
ngOnDestroy
Component 銷毀前執行, 通常用於清理資源、取消訂閱.
export class MyComponent implements OnInit, OnDestroy {
private subscription: Subscription;
ngOnInit(): void {
this.subscription = this.dataService
.getData()
.subscribe((data) => console.log(data));
}
ngOnDestroy(): void {
// 取消訂閱,避免記憶體洩漏
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}
ngAfterViewInit
視圖初始化完成後執行, 可以安全地訪問 DOM 元素.
export class MyComponent implements AfterViewInit {
@ViewChild("myInput") myInput: ElementRef;
ngAfterViewInit(): void {
// 這時可以安全地訪問 DOM 元素
this.myInput.nativeElement.focus();
}
}
constructor vs ngOnInit
很多人會疑惑: 什麼時候用 constructor, 什麼時候用 ngOnInit?
constructor
- ES6 的 class 建構子
- 用於依賴注入 (Dependency Injection)
- 此時 Component 的屬性可能還未初始化
- 不應該在這裡執行複雜的邏輯或 HTTP 請求
constructor(
private userService: UserService,
private router: Router
) {
// 只做簡單的初始化
console.log('Component created');
}
ngOnInit
- Angular 的生命週期 Hook
- Component 的輸入屬性已經初始化完成
- 應該在這裡執行資料載入、訂閱等操作
ngOnInit(): void {
// 執行資料初始化
this.loadData();
}
實用技巧
1. 使用 OnPush 策略優化效能
搭配 ngOnChanges 使用 ChangeDetectionStrategy.OnPush 可以提升效能.
@Component({
selector: "app-my-component",
templateUrl: "./my-component.component.html",
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MyComponent implements OnChanges {
@Input() data: any;
ngOnChanges(changes: SimpleChanges): void {
// 只有當 input 改變時才會觸發變更檢測
}
}
2. 避免在 ngDoCheck 中執行繁重操作
ngDoCheck 會在每次變更檢測時執行, 頻率非常高, 應避免執行繁重的操作.
3. 記得取消訂閱
在 ngOnDestroy 中取消所有訂閱, 避免記憶體洩漏.
private destroy$ = new Subject<void>();
ngOnInit(): void {
this.dataService.getData()
.pipe(takeUntil(this.destroy$))
.subscribe(data => console.log(data));
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
總結
理解 Angular 的生命週期 Hook 是開發 Angular 應用的關鍵:
- ngOnInit: 最常用, 用於初始化資料
- ngOnChanges: 監聽 Input 屬性變化
- ngOnDestroy: 清理資源, 避免記憶體洩漏
- ngAfterViewInit: 訪問 DOM 元素
- constructor vs ngOnInit: constructor 用於依賴注入, ngOnInit 用於資料初始化
掌握這些生命週期 Hook 的執行順序和調用時機, 能夠更好地理解 Angular 中 Component 和 Directive 的運行機制, 寫出更高效且穩定的應用程式.