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 生命週期掛鉤會在過渡結束後觸發。
