# Day23 【牙起來】 元件執行順序 & 生命週期 - Angular 再繼續往RxJS的道路之前, 先插播一則文章,回過頭來看Angular元件、服務、生命週期與執行的順序 ## constructor、ngOnInit 透過`ng cli`產出的元件,底下都會有以下兩個方法(Method) * `constructor()` Typescript的構建物件函式 * `ngOnInit()` Angular元件初始化時執行 在初學階段,這兩者都可以視作**元件初始化時**做的事情 只要知道`ngOnInit`執行的時機點晚於`constructor`一些 因為`constructor()` Typescript層的東西 其中小括號內`()`是構建函式的**Input參數**(Parameter) 把這個物件所需要用到、所依賴的**服務**注入進來,提供給這整個物件使用(讓物件內的每個方法都可以取用到) 昨天看到的 `HttpClient` 是Angular內建的眾多服務(Service)之一,就是透過**注入服務**到元件中讓元件能使用該服務底下的方法。未來我們也會自行寫服務`service.ts`,注入到自己寫的元件中。 所以執行順序為, 先執行構建物件,等構建完畢之後才執行Angular元件初始化 ```typescript= constructor() { console.log('==== constructor ====') } ngOnInit(): void { console.log('==== ngOnInit ====') } ``` ![](https://i.imgur.com/Ss3bV8g.png) ## 生命週期、執行順序 除了以上兩種預設的方法之外,還有很多**內建方法** ```typescript= export class AppComponent implements OnInit, OnDestroy, AfterViewInit { constructor() { console.log('==== constructor ====') } ngOnInit(): void { console.log('==== ngOnInit ====') } ngAfterViewInit(): void { console.log('==== ngAfterViewInit ====') } ngOnDestroy() { console.log('==== ngOnDestroy ====') } } ``` > 這一行 `implements OnInit, OnDestroy, AfterViewInit` 實作介面 > 有加、沒加都不會對畫面產生影響 > > 因為`Javascript`沒有`interface`,最終編譯都是相同結果 > 只是加了對程式碼風格較好,有助於IDE推斷介面型別 > > ![](https://i.imgur.com/s9i5fYr.gif) 執行結果 ![](https://i.imgur.com/XjBskKa.png) * `constructor` 構建一個物件首先執行的函式,在這裡引入物件所需用到的服務 * `ngOnInit` 生命週期 元件初始化,進入此元件做的事情,此時畫面尚未生成完畢 * `ngAfterViewInit` 生命週期 當畫面(View)渲染完畢才做的事 > 例如抓取頁面上的某個節點,若在畫面出現之前執行的話會抓不到元素而變成`undefined` * `ngOnDestroy` 生命週期 離開、結束此元件時做的事情 > 刷新頁面或離開頁面**都不會觸發效果** > 因為是這兩件事情主導權不在Angular手上,而是由瀏覽器銷毀 > 在未來提及`Rouing`切換頁面元件時可以看到效果 以人類的生命週期來對應的話,分別是 * 受精階段: `constructor` 建構胚胎卵,注入所需要的養分服務,以幫助未來成長 * 胎兒階段: `ngOnInit` 元件在媽媽的肚子裡面長好了 * 嬰兒階段: `ngAfterViewInit` 畫面生出來了,我們肉眼看的見四肢了 * 死亡階段: `destroy` 生命離開肉體軀殼,被大自然回收了 ## 巢狀結構的執行順序 我們在 `app` 與 `store` 兩個元件中,都分別印出以上四種狀態 修改 `store.component.ts` ```typescript= ... export class StoreComponent implements OnInit, AfterViewInit, OnDestroy { constructor() { console.log('==== StoreComponent constructor ====') } ngOnInit(): void { console.log('==== StoreComponent ngOnInit ====') } ngAfterViewInit(): void { console.log('==== StoreComponent ngAfterViewInit ====') } ngOnDestroy(): void { console.log('==== StoreComponent ngOnDestroy ====') } } ``` 修改 `app.component.ts` ```typescript= export class AppComponent implements OnInit, AfterViewInit, OnDestroy { title = 'project03'; constructor() { console.log('==== AppComponent constructor ====') } ngOnInit(): void { console.log('==== AppComponent ngOnInit ====') } ngAfterViewInit(): void { console.log('==== AppComponent ngAfterViewInit ====') } ngOnDestroy(): void { console.log('==== AppComponent ngOnDestroy ====') } } ``` 修改 `app.component.html` ```html= <app-store></app-store> ``` 結果畫面 ![](https://i.imgur.com/TqEhQ3m.png) 可以看到巢狀的執行順序 先是**App**的建構,建構完畢跑進**Store**開始建構,接著開始Angular的生命週期, 從**APP**開始初始化,等**Store**初始化好了、畫面都好了,最終**APP**的畫面才好。 ## 加上服務的執行順序 透過`ng cli`建立一個名為`svc`的服務 > ng g s svc 修改`svc.service.ts` ```typescript= import { Injectable, OnInit } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class SvcService implements OnInit{ constructor() { console.log('SvcService constructor') } } ``` 接著分別在`app.component.ts` 與`store.component.ts` 中 加上這一段試試看效果 ```typescript= constructor(svc: SvcService) { } ``` * 僅將`svc: SvcService`加在 `app.component.ts` 的結果 ![](https://i.imgur.com/gFY4jNs.png) * 僅將`svc: SvcService`加在 `store.component.ts` 的結果 ![](https://i.imgur.com/q0OyTDZ.png) 由此可見,注入的服務是在`Construct()`的前一刻先被執行 --- ## 服務(Service)沒有元件生命週期 服務(Service)沒有**Angular生命週期** 就算加了`ngOnInit`等方法,也不會有任何作用 **Angular生命週期**只有元件(Component)、指示(Directive)有 ```typescript= import { Injectable, OnInit } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class SvcService implements OnInit{ constructor() { console.log('SvcService constructor') } ngOnInit() { console.log('SvcService ngOnInit') } } ```