apps的生命週期對於操做系統管理系統資源來講相當重要。在Android、IOS和Windows等的平臺上,系統能夠隨意啓動和停止apps的運行,這使得這些平臺可以從新分配資源,以提供最好的操做體驗給用戶。javascript
但對於web應用,並無相似的生命週期機制。隨着打開網頁數量增長,內存、CPU、網絡吞吐等系統關鍵資源都被過分使用,致使系統性能降低,下降了用戶的操做體驗。html
雖然瀏覽器很早以前就有提供關於生命週期的事件,好比load
、unload
和visibilitychange
等,可是這些事件只容許開發人員響應用戶自行發起的生命週期狀態更改。爲了讓網頁更可靠地運行,尤爲是在低功耗的設備(手機、智能手錶等),瀏覽器須要一種主動回收和分配系統資源的機制。java
Page Lifecycle API就是爲了解決這些問題而提出的方案,目標主要有三點:git
Page Lifecycle API
已經在Chrome 68上獲得支持,下面將會進行詳細介紹。github
Page Lifecycle API
定義了規範化的app生命週期狀態,且每一個頁面在一個階段只能處於一個狀態,每一個狀態的改變都會有相應的事件被觸發。話很少說,先上圖:web
頁面狀態有6個,下面將分別展開描述,from表明的是前一個頁面狀態,to表明的是下一階段可能會變動到的狀態。windows
Active
若是頁面可見並具備輸入焦點,則該頁面處於Active
狀態api
from:passive
(focus事件)瀏覽器
to: passive
(blur事件)緩存
Passive
若是頁面可見並不具備輸入焦點,則該頁面處於Passive
狀態
from: avtive
(blur事件)、hidden
(visibilitychange事件)
to: avtive
(focus事件)、hidden
(visibilitychange事件)
Hidden
若是頁面不可見且不處於frozen
狀態,則該頁面處於hidden
狀態
from: passive
(visibilitychange事件)
to: passive
(visibilitychange事件)、frozen
(freeze事件)、terminated
(pagehide事件)
Frozen
若是網頁處於frozen
狀態下,瀏覽器將暫停執行頁面任務隊列中的可凍結任務,直到頁面被解除凍結。這意味着像定時器和回調函數這樣的任務不會運行。
from: hidden
(freeze事件)
to: active
(resume和pageshow事件)、passive
(resume和pageshow事件)、hidden
(resume事件)
Terminated
若是頁面開始被瀏覽器卸載並從內存中清除,它就處於terminated
狀態。在此狀態下不能啓動任何新任務,若是現有運行時間太長,可能會被提早終止。
from: hidden
(pagehide事件)
to: None
Discarded
當瀏覽器爲了節省資源而卸載頁面時,它處於discarded
狀態。任何類型的任務、事件回調或JavaScript代碼都不能在這種狀態下運行,由於丟棄一般發生在資源約束下,能夠理解爲頁面被動關閉,瀏覽器主動釋放資源。
from: frozen
(無事件觸發)
to: None
*表明的是Page Lifecycle API
新提供的事件
foucs
DOM元素獲取焦點,前一個狀態通常是passive
,當前狀態是avtive
blur
DOM元素失去焦點,前一個狀態通常是active
,當前狀態是passive
visibilitychange
document
的visibilitySatate
屬性發生變動,當用戶導航到新頁面、切換選項卡、關閉選項卡、最小化或關閉瀏覽器、或切換移動操做系統上的應用程序時,會觸發事件。前一個狀態是passive
或hidden
,,當前狀態是passive
或者hidden
freeze*
頁面被凍結,任務隊列中的可凍結任務都中止運行。前一個狀態是hidden
,當前狀態是frozen
resume*
頁面解除凍結狀態,前一個狀態是frozen
,當前狀態是active
、passive
或者hidden
pageshow
當一條會話歷史記錄被執行的時候將會觸發事件,包括了後退(前進)按鈕操做,同時也會在onload
事件觸發後初始化頁面時觸發。前一個狀態可能爲frozen
,當前狀態爲active
、passive
或hidden
pagehide
與pageshow
相似,不一樣的就是導航離開當前網頁時觸發。先前的狀態可能爲hidden
,當前狀態可能爲frozen
或者terminated
beforeunload
窗口、文檔及其資源即將被卸載。文檔仍然可見,此時事件仍然能夠取消。前一個狀態可能爲hidden
,當前狀態爲terminated
unload
卸載頁面時觸發,前一個狀態可能爲hidden
,當前狀態爲terminated
上面的圖表顯示了兩種系統觸發的頁面狀態:frozen
和discard
,這種頁面狀態和用戶觸發的不同,開發者沒法主動感知狀態變動。可是在Chrome 68上,開發者能夠監聽freeze
和resume
兩個事件處理頁面狀態變動。
document.addEventListener('freeze', (event) => {
// 頁面處於凍結狀態
});
document.addEventListener('resume', (event) => {
// 頁面解凍
});
複製代碼
同時document
對象新增了wasDiscarded
屬性,這個屬性表明頁面是否處於discarded
狀態,開發者能夠根據這個屬性的值處理不一樣的邏輯
if (document.wasDiscarded) {
// 頁面被瀏覽器丟棄
}
複製代碼
// active、passive和hidden三種狀態
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
複製代碼
另外,frozen
和和terminated
的狀態變動能夠經過監聽freeze
和pagehide
事件獲取。
// 保存初始頁面狀態
let state = getState();
// 記錄狀態變動並打印在控制檯
// 更新當前頁面狀態
const logStateChange = (nextState) => {
const prevState = state;
if (nextState !== prevState) {
console.log(`State change: ${prevState} >>> ${nextState}`);
state = nextState;
}
};
// 監聽生命週期事件,保持頁面狀態更新
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
window.addEventListener(type, () => logStateChange(getState()), {capture: true});
});
// 監聽freeze事件
window.addEventListener('freeze', () => {
// 頁面狀態變動爲frozen
logStateChange('frozen');
}, {capture: true});
window.addEventListener('pagehide', (event) => {
if (event.persisted) {
// event.persisted爲true意味着頁面是從緩存中加載的,因此是frozen狀態
logStateChange('frozen');
} else {
// terminated狀態
logStateChange('terminated');
}
}, {capture: true});
複製代碼
Page Lifecycle
這個標準剛剛引入,並無獲得所有瀏覽器平臺的支持。有些瀏覽器可能在切換標籤的時候不會觸發blur
事件,有些瀏覽器沒有實現freeze
和resume
事件,IE10如下版本不支持visibilitychange
事件等等...
爲了讓開發者更容易上手處理跨平臺兼容的問題,谷歌開發了PageLifecycle.js這個庫,用於觀察頁面生命週期狀態的變化。PageLifecycle.js按事件觸發順序規範化處理了跨瀏覽器的差別,保證狀態變動和標準規範保存一致。
對於開發者來講,理解頁面生命狀態並懂得根據頁面狀態不懂執行不一樣業務邏輯是很是重要的,下面介紹各個狀態下的最佳實踐:
Active
active
狀態是用戶最關鍵的時間,所以也是頁面響應用戶輸入最重要的時間,任何可能阻塞主線程執行的非UI渲染任務均可以放到requestidlecallback
或者web worker
執行
Passive
在此狀態下,用戶沒有和頁面進行交互,但頁面仍然處於可視狀態,因此UI和動畫須要保持渲染狀態,從active
狀態過渡到passive
是一個保存頁面狀態的好時機,好比一些表單值。
Hidden
頁面被隱藏或者關閉,中止全部與用戶交互、UI渲染有關的任務,並及時保存應用狀態
Frozen
在frozen
狀態下,可凍結任務將會被掛起直到頁面解凍(有可能永遠不會發生)。在這個狀態下,開發者要作如下幾點:
Terminated
一般在此狀態下不須要處理任何任務,由於這個階段的任務不能保存可靠執行,有可能被強行終止。但你也能夠作一些狀態持久化或者埋點分析的任務