網站搜尋

angular - ionViewWillEnter 與 ionViewDidEnter


當我們不想重新啟動已經快取的任務時,毫無疑問我們會使用 ionDidLoad。

現在,假設我們每次進入一個視圖時都需要執行一個任務,我們應該如何選擇使用 ionViewWillEnter 還是 ionViewDidEnter。

我沒有找到任何明確的指引。

例如:
1) 取得 navParams
2) 呼叫 REST API
3) 宣告變數
4) 在 DOM 元素完全載入後啟動某些功能(例如:Google 地圖初始化)

附加說明:
我不是在尋找明確寫在 Ionic 文件中的答案。我想知道的是放在 ionViewWillEnter 與 ionViewDidEnter 的優缺點分別是什麼。

例如:在 ionViewDidEnter 中啟動會導致一些延遲(例如,我不確定這一點)——即使是微不足道的延遲

答案#1

sebaferreras 的回答很好,但它缺少一些要點。

首先,關於 Ionic 的生命週期。如果你把所有 Ionic 事件都寫下來,並在每個事件中放一個 console.log,你將會看到以下情景:

constructor --> ionViewDidLoad --> ionViewWillEnter --> ionViewDidEnter --> ionViewWillLeave --> ionViewDidLeave --> ionViewWillUnload.

constructor 在頁面初始化時首先運行。這是你宣告變數預設值的最佳位置。
ionViewDidLoad 在視圖完全加載時觸發。這意味著你可以在這裡附加 DOM 元素。

ionViewWillEnter 在頁面 即將進入 並成為活動頁面時運行。ionViewDidEnter 在頁面 完全進入 並成為活動頁面時運行。ionViewDidEnter 將在 ionViewWillEnter 中的 所有 同步 完成後觸發。為了證明這點,只需將大量程式碼放入 ionViewWillEnter:

ionViewWillEnter(){
    console.log("ionViewWillEnter")
    for(let i = 0; i < 100000; i++){
      console.log(i);
    }
}
ionViewDidEnter(){
    console.log("ionViewDidEnter")
}

運行你的程式碼,你會看到它啟動頁面需要很長時間。所以千萬不要將繁重的 同步 代碼放入 ionViewWillEnter。只在 ionViewWillEnter 使用 非同步,並將所有 同步 代碼移到 ionViewDidEnter。因為在那裡,你的頁面已進入,這將帶來更好的使用者體驗。

答案#2

答案非常簡單,而且一如既往,了解背後發生了什麼的最佳方法是檢查 Ionic 的源代碼。

長話短說:ionViewWillEnter 生命週期鉤子會在頁面轉換開始前被觸發,而 ionViewDidEnter 則會在轉換結束後被觸發。請查看此答案的最後部分以參考源代碼的引用。

那麼,你應該在什麼時候使用這些 hook 呢?可能會有幾種情況,所以這裡只是基於我參與過的一些應用提供的幾個想法。

  • 有時候,你可能希望每次使用者打開頁面時都更新頁面上的一些資訊;那可能需要向遠端 API 發出 HTTP 請求以取得一些資料。在這種情況下,我們可以說使用 IonViewWillEnter 看起來是正確的選擇,這樣你就可以盡快發出請求,並更早獲得回應。

  • 同樣的情況也適用於當你因某些原因需要與 DOM 互動時,因為在執行 IonViewWillEnter 時 DOM 已經被加載,因此這可以幫助你盡快初始化頁面,而且如果運氣好,頁面可能會顯示給使用者,準備好使用。

  • 當你需要用從上一個視圖獲取的某些參數來初始化視圖時,同樣的方法也是有效的。在這種情況下,因為這是一個非常簡單的操作,你可以使用 ionViewWillEnter 生命週期鉤子,視圖將已經初始化完畢並顯示給用戶。

那麼,你應該在什麼時候使用 ionViewDidEnter 呢?

  • 我注意到有時在非常舊的 Android 裝置上執行應用程式(記憶體小於 512MB - 1GB)時,當在 ionViewWillEnter 發送 API 請求時,動畫可能會有點延遲,因此將這些 API 請求放在 ionViewDidEnter 會是一個好主意,以確保動畫已經結束

  • 在一些應用程式中,我被要求在被推送的頁面元素中加入一些華麗的動畫,例如從底部帶入部分內容、延遲後淡入其他元素等等。你可以使用 Angular 動畫來實現這些效果(以便能控制時間),而避免這些動畫出現卡頓的最佳方式是使用 ionViewDidEnter 鉤子。這也能讓使用者感覺到該頁面內元素的動畫是頁面本身過渡的延續。

你可以看看 NavControllerBase 代碼:

1) IonViewWillEnter

// around line 666...
_transitionStart(...): Promise<NavResult> {

    // ...

    // around line 700...

    // create a callback that needs to run within zone
    // that will fire off the willEnter/Leave lifecycle events at the right time
    transition.beforeAddRead(this._viewsWillLifecycles.bind(this, enteringView, leavingView));

   // ...

}

然後…

  // around line 805...
  _viewsWillLifecycles(enteringView: ViewController, leavingView: ViewController) {
    if (enteringView || leavingView) {
      this._zone.run(() => {
        // Here, the order is important. WillLeave must be called before WillEnter.
        leavingView && this._willLeave(leavingView, !enteringView);
        enteringView && this._willEnter(enteringView);
      });
    }
  }

如果你檢查 animation.d.ts 定義中的 beforeAddRead 方法,你會看到:

/**
* Add a function which contains DOM reads, which will run
* before the animation begins.
*/
beforeAddRead(domReadFn: Function): Animation;

所以現在我們可以確定,ionViewWillEnter 生命周期钩子會在頁面轉換開始之前執行

2) IonViewDidEnter

這一個簡單一點。再次在同一個 NavControllerBase 中:

// around line 743...
  _transitionFinish(...): NavResult {

    // ...

    // around line 753...
    if (hasCompleted) {
      // transition has completed (went from 0 to 1)
      if (enteringView) {
        enteringName = enteringView.name;
        this._didEnter(enteringView);
      }

      // ..

    }

    // ...

  }

和

// around line 939...
  _didEnter(view: ViewController) {
    assert(this.isTransitioning(), 'nav controller should be transitioning');
    assert(NgZone.isInAngularZone(), 'callback should be zoned');

    try {
      view._didEnter();
      this.viewDidEnter.emit(view);
      this._app.viewDidEnter.emit(view);
    } catch (e) {
      this._errHandler && this._errHandler.handleError(e);
    }
  }

所以因此,我們可以說 ionViewDidEnter 生命週期掛鉤會在過渡結束後觸發。