1. 程式人生 > >【譯】頁面生命週期API

【譯】頁面生命週期API


現代瀏覽器有時在系統資源受限時,會出現頁面卡死或完全丟棄頁面的情況。 在未來,瀏覽器希望讓使用者可以消耗更少的資源和記憶體,更加積極主動地做這件事。頁面生命週期 API,執行在 Chrome 68 中,提供了生命週期的鉤子,你可以在不影響使用者體驗的情況下,安全的去處理瀏覽器響應。 接下來我們就詳細的瞭解一下這個 API,看看你是否會在應用中嘗試使用這些特性。

原文連結:https://developers.google.com/web/updates/2018/07/page-lifecycle-api

背景資料

現代作業系統管理資源的主要方式就是應用程式生命週期。 像在 Android

iOS 和近期的 Windows 版本上,應用程式可以在任何時候被作業系統啟動和終止。 平臺能夠針對使用者操作,進行精簡或重新分配資源。

在網頁上,一直沒有這樣的生命週期,應用程式就會被無限期地儲存下來。 但隨著大量網頁的執行,諸如記憶體、 CPU、電池和網路等關鍵系統資源被超額訂閱,會導致非常糟糕的終端使用者體驗。

儘管網頁長期以來都有與生命週期狀態有關的事件—例如載入、解除安裝和視覺化變化—但這些事件只允許開發人員響應使用者發起的生命週期狀態變化。為了使網路能夠在低功率裝置上可靠地工作(並且在所有平臺上都有更多的資源意識) ,瀏覽器需要一種主動回收和重新分配系統資源的方法。

事實上,現在的瀏覽器已經採取了積極的措施來節省後臺標籤頁面的資源,而且許多瀏覽器(特別是 Chrome)想要做更多的事情—來減少他們的整體資源足跡。

但問題是,開發者目前既不知道能去幹預,也不知道怎樣去這些系統發起干預措施。 同時, 也需要考慮到瀏覽器是要保守推進,還是冒著被破壞的風險。

頁面生命週期 API
試圖通過以下方式解決這個問題:

- 在網頁上引入生命週期狀態概念並使之標準化
- 定義新的系統啟動狀態,允許瀏覽器通過隱藏或不活躍來限制標籤所消耗的資源
- 建立新的 API 和事件,使 web 開發人員能夠對這些新系統啟動狀態的轉換做出響應

這個解決方案提供了 web 開發者建立應用程式所需要的可預見性,它允許瀏覽器更積極地優化系統資源,最終使所有網路使用者受益。

這篇文章的其餘部分將介紹執行在 Chrome 68 瀏覽器中的新的頁面生命週期特性,並探索它與所有現有的 web 平臺狀態和事件之間的關係。 也會為開發工作者提供在每個狀態下操作的建議和最佳實踐。

頁面生命週期狀態和事件概述

所有頁面的生命週期狀態是離散和相互獨立的,這意味著一個頁面同一時間只能處於一個狀態。 對頁面生命週期狀態的大多數更改通常可以通過 DOM 事件來觀察(請參閱開發者對每個狀態的異常建議)。

也許使用圖表是解釋頁面生命週期狀態的最簡單的方法——以及它們之間狀態轉換的事件:

這裡寫圖片描述

狀態


下表詳細解釋了每個狀態。 它還列出了可能出現的狀態,以及開發人員可以用來觀察變化的事件。


狀態名 描述
Active 如果頁面是可見的並且有輸入焦點,那麼它就處於啟用狀態。

可能的上一個狀態: passive (通過 focus 事件)
可能的下一個狀態: passive (通過 blur 事件)
Passive 如果頁面可見且沒有輸入焦點,則頁面處於被動狀態。

可能的先前狀態: active (通過 blur 事件) hidden (通過 visibilitychange 事件)
可能的下一個狀態: active (通過 focus 事件) hidden (通過 visibilitychange 事件)
Hidden 如果頁面不可見且未被凍結,則頁面處於隱藏狀態。

可能的先前狀態: passive 狀態(通過visibilitychange事件)
可能的下一個狀態: passive (通過visibilitychange事件) frozen (通過 freeze 事件) terminated (通過頁面pagehide事件)
Frozen 在凍結狀態下,瀏覽器將會暫停執行任務佇列中的任務,直到頁面解凍。 就像 JavaScript 的定時器一樣,回撥函式不會立即執行。 已經在執行的任務可以完成(一般主要指的是凍結回撥) ,但是會在可以執行哪些方法, 可以執行多長時間上可能會受限制。
瀏覽器將頁面凍結,是儲存 CPU/電池/資料使用的一種方式; 同時,也是為了讓瀏覽器可以快速的返回/跳轉顯示頁面ーー避免全頁面重新載入。

可能的先前狀態: hidden(通過freeze事件)
可能的下一個狀態: active(通過resume事件,然後 pageshow 事件)passive(通過resume事件,然後是 pageshow 事件)
Terminated 一旦瀏覽器開始解除安裝和清除記憶體,頁面就處於終止狀態。 在這種狀態下,沒有新的任務可以開始,如果進度任務跑得時間太久, 可能被殺死。

可能的先前狀態: hidden(通過pagehide事件)
可能的下一個狀態: 沒有
Discarded 為了節約資源,當頁面被瀏覽器解除安裝時,處於丟棄狀態。 任何任務、事件回撥或任何型別的 JavaScript 都不能在這種狀態下執行,因為拋棄通常發生在資源約束下,在這種情況, 開始新的程序是不可能的。
在被丟棄的狀態,即使頁面已經消失,標籤本身(包括標籤標題和 favicon)還是是可見的。

可能的先前狀態: frozen (沒有事件發生)
可能的下一個狀態: 沒有


事件


瀏覽器分發了大量事件,但只有一小部分可能會引起頁面生命週期狀態發生變化。 下表概述了所有與生命週期相關的事件,並列出了它們轉換過程。


名稱 詳情
focus 一個 DOM 元素已經收到了焦點。

> 注意:Focus 事件不一定意味著頁面狀態變化。只是會發出一個訊號,說從沒聚焦變為聚焦。
可能的先前狀態: 被動
可能的當前狀態: 活躍狀態
focus 一個 DOM 元素已經收到了焦點。
注意:Focus 事件不一定意味著頁面狀態變化。只是會發出一個訊號,說從沒聚焦變為聚焦。

可能的先前狀態: passive
可能的當前狀態: active
blur 一個 DOM 元素已經失去了焦點。

> 注意: Blur 事件不一定意味著頁面狀態變化。 只是提醒說頁面現在不處於聚焦狀態了。(即頁面沒有從一個 focus 狀態切換到另一個元素focus )

可能的先前狀態: active
可能的當前狀態: passive
visibilitychange 該文件的可視狀態值發生改變。當用戶開啟一個新頁面,切換標籤,關閉標籤,最小化或關閉瀏覽器,或者在移動作業系統上切換應用程式時,這種情況就會發生。

可能的先前狀態: passive hidden
可能的當前狀態: passive hidden
freeze * 該頁面已經被凍結了。 頁面任務佇列中的任何可凍結任務都不會啟動。

可能的先前狀態: hidden
可能的當前狀態: frozen
resume * 瀏覽器恢復了一個凍結頁面。

可能的先前狀態: frozen
可能的當前狀態:
(1) active (如果後面跟著 pageshow 事件)
(2) passive (如果後面是 pageshow 事件)
(3) hidden
pageshow 會生成一條瀏覽器歷史記錄。
這可能是一個全新的頁面載入,也可以是從瀏覽器快取中獲取的頁面。 如果頁面是從快取中獲取的,則該事件的 persisted 屬性值為 true,否則為 false

可能的先前狀態: frozen (也可能是 resume )
頁面當前狀態: active passive hidden
pagehide 頁面隱藏,即將跳轉。
如果使用者開啟另一個頁面,瀏覽器可以將當前頁新增到瀏覽器快取內,以便稍後重用,那麼該事件的 persisted 屬性值為 true。 如果是 true,頁面將進入 freeze,否則它將進入 terminated 狀態。

可能的先前狀態: hidden
可能的當前狀態:
frozen (event.persistedtrue, 就是 freeze狀態 )
terminated (event.persistedfalse, 觸發 unload 事件)
beforeunload window, document 和頁面內的資源可能會被解除安裝。 document 仍然是可見的, 而且可以取消該操作。

警告: beforeunload 只能提示使用者沒有儲存的改變。 一旦改變已經被儲存下來, 這個事件應該被刪除。不能無條件的新增到頁面中,會嚴重影響頁面效能。詳情請參閱 legacy APIs section

可能的先前狀態: hidden
unload 頁面正在被解除安裝。

警告: 任何時候都不建議使用 unload, 因為會影響頁面效能。 詳情請參閱 legacy APIs section

可能的先前狀態: hidden
可能的當前狀態: terminated


* 解釋頁面生命週期 API 定義的新事件 *

68 新增功能

上圖中有兩個狀態是瀏覽器初始化形成的,而不是使用者觸發的: frozendiscarded。 像上述提到的一樣,現在的瀏覽器會慎重的去選擇凍結或者丟棄隱藏標籤頁, 但開發者並不知道他們什麼時候發生。

Chrome 68 中,開發人員現在可以通過監聽 freeze 和在文件上呼叫 resume 事件來觀察隱藏的標籤頁何時被凍結, 被解凍。

document.addEventListener('freeze', (event) => {
  // The page is now frozen.
});
​
document.addEventListener('resume', (event) => {
  // The page has been unfrozen.
});


Chrome 68 中,文件物件現在也包含了一個 wasDiscarded 屬性。 為了確定在隱藏選項卡中是否有頁面被丟棄,您可以在頁面載入時檢查該屬性的值(注意: 丟棄的頁面必須重新載入才可以再次使用)。

if (document.wasDiscarded) {
  // Page was previously discarded by the browser while in a hidden tab.
}


關於在凍結和恢復事件中什麼事情是重要的,以及如何處理和準備被丟棄的頁面,請參閱開發者對每個狀態的建議

接下來的幾個部分概述這些新特性如何適應現有的網頁平臺的狀態和事件。

用程式碼觀察頁面生命週期狀態

activepassivehidden 狀態中,執行 JavaScript 程式碼可以從現有的 Web 平臺 Api 中確定當前頁面的生命週期狀態。

const getState = () => {
  if (document.visibilityState === 'hidden') {
    return 'hidden';
  }
  if (document.hasFocus()) {
    return 'active';
  }
  return 'passive';
};


另一方面,被凍結和終止的狀態只能在它們各自的事件監聽器(凍結和頁面隱藏)中檢測到。

觀察狀態變化

在上面定義的 getState 函式的基礎上,您可以使用下面的程式碼觀察所有頁生命週期狀態的變化。

// Stores the initial state using the `getState()` function (defined above).
let state = getState();
​
// Accepts a next state and, if there's been a state change, logs the
// change to the console. It also updates the `state` value defined above.
const logStateChange = (nextState) => {
  const prevState = state;
  if (nextState !== prevState) {
    console.log(`State change: ${prevState} >>> ${nextState}`);
    state = nextState;
  }
};
​
// These lifecycle events can all use the same listener to observe state
// changes (they call the `getState()` function to determine the next state).
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
  window.addEventListener(type, () => logStateChange(getState()), {capture: true});
});
​
// The next two listeners, on the other hand, can determine the next
// state from the event itself.
window.addEventListener('freeze', () => {
  // In the freeze event, the next state is always frozen.
  logStateChange('frozen');
}, {capture: true});
​
window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    // If the event's persisted property is `true` the page is about
    // to enter the page navigation cache, which is also in the frozen state.
    logStateChange('frozen');
  } else {
    // If the event's persisted property is not `true` the page is
    // about to be unloaded.
    logStateChange('terminated');
  }
}, {capture: true});
The above code does three things:


上面的程式碼可以做三件事:

- 用函式 getState() 初始化狀態
- 定義一個函式接受下一個狀態,如果有變化,則將狀態更改記錄列印到控制檯
- 為所有必要的生命週期事件新增事件監聽器,在狀態變化時呼叫 logStateChange()

警告: 這段程式碼在不同的瀏覽器中產生不同的效果, 因為事件的執行順序(和準確性)沒有得到一致的實現。要學習如何最好地處理這些矛盾,請參見管理跨瀏覽器的差異.

關於上述程式碼需要注意的一點是,所有事件監聽器會被新增到 window 中,而且設定了 {capture: true}。 原因有以下幾點:

- 並不是所有的頁面生命週期事件都有相同的目標源。pagehidepageshowwindows 上觸發。visibilitychange, freezeresume 是在 document 上, focusblur 是在他們各自的 DOM 元素上觸發。
- 大多數這些事件不會產生冒泡,這意味著不能將非捕獲事件監聽新增一個有共同祖先的元素上,去觀察所有元素。
- 事件捕獲和事件冒泡階段在目的碼執行之前,因此新增監聽器有助於可以在其他程式碼執行前,中斷執行。

管理跨瀏覽器的差異


本文開頭的圖表根據頁面生命週期 API 概述了狀態和事件流。 但是由於這個 API 剛剛被引入,新的事件和 DOM API 還沒有在所有的瀏覽器中實現。

此外,今天在所有瀏覽器中實現的事件也並沒有統一實現方案。 例如:

- 當切換 Tab 的時候,一些瀏覽器是不觸發 blur 的。也就意味著這個頁面可以不經過 passive 狀態,從 active 變為 hidden 狀態。
- 一些瀏覽器完善了 頁面導航快取。 在頁面生命週期 API 中定義快取頁面為 frozen 狀態。
由於這個 API 是全新的,儘管通過 pagehidepageshow 事件可以觸發這個狀態,但有些瀏覽器還是沒有實現 freezeresume 事件。

- IE10 以下的老版本瀏覽器,不支援 visibilitychange 事件。
- pagehidevisibilitychange 事件的執行順序有被調整。如果頁面處於可見狀態,正打算被解除安裝,先前的瀏覽器是先出發 visibilitychange , 然後觸發 pagehide。 新版本的 chrome, 則相反,不區分頁面是否處於要解除安裝狀態。

為了使開發人員更容易地處理這些跨瀏覽器的相容性問題,專注於遵循生命週期狀態推薦和最佳實踐,我們釋出了一個用於觀察頁面生命週期 API 狀態變化的 JavaScript 庫, PageLifecycle.js,。

PageLifecycle.js 將事件點選順序中的跨瀏覽器差異規範化,使得在所有瀏覽器裡,狀態變化都和圖表中的保持一致。

每個狀態的開發推薦

作為開發人員,理解頁面生命週期狀態和知道如何在程式碼中觀察它們是很重要的,因為你應該(而且不應該)做的工作在很大程度上取決於你的頁面處於什麼狀態。

例如,如果頁面處於隱藏狀態,顯然不能將暫時通知顯示給使用者。雖然這個例子很明顯,但是還有其他一些建議不那麼明顯,但值得列舉的。

狀態 開發者建議
Active 對於使用者來說,active 狀態是最關鍵的時間,是響應使用者輸入的最重要時間

任何可能阻止主執行緒的非 UI工作應該被剝奪到空閒時間或者取消網路工作
Passive 在被動狀態下,使用者不與頁面進行互動,但是他們仍然可以看到它。 這意味著使用者介面的更新和動畫應該仍然是平滑的,但是這些更新發生的時間並不那麼重要。

當頁面從主動變為被動時,這個時間最好是維持未儲存的應用程式的狀態。
Hidden 當頁面從被動變為隱藏時,使用者可能在重新載入之前不會再與它進行互動。

過渡到隱藏狀態時, 通常也是開發人員可以觀察到的最後一個狀態變化(這在移動裝置上更明顯,因為使用者可以關閉標籤頁或者瀏覽器應用程式本身,而且 beforeunloadpagehideunload 事件在這些情況下不會被觸發)。

也意味著你應該將隱藏狀態視為使用者會話可能結束了。 換句話說,應該做到保留任何未儲存的應用程式狀態併發送任何未傳送的分析資料。

你也應該停止更新使用者介面(因為使用者不會看到它們) ,停止任何使用者不想在後臺執行的任務。
Frozen 在凍結狀態下,任務佇列中自由執行的任務將暫停,直到頁面解凍,也可能永遠都不解凍(例如,如果頁面被丟棄)。這意味著當頁面從隱藏變為凍結時,你必須停止任何計時器或者關閉任何連線,這些連線如果被凍結,可能會影響到同一來源的其他開啟的標籤頁,或者影響瀏覽器將頁面導航快取的功能。

尤其重要的是,你要:
- 關閉所有已經開啟的 IndexedDB 連線
- 關閉所有 BroadcastChannel 連線
- 關閉已啟用的WebRTC
- 停止所有網路輪詢,關閉任何已開啟的 Web Socket
- 釋放所有 Web Locks

如果你還想在頁面被拋棄或者重新載入後,恢復資料,那麼您還應該堅持將任何動態檢視狀態(例如無限列表檢視中的滾動位置)儲存到 sessionStorage (或者通過(IndexedDB)提交

如果頁面從凍結變為隱藏,您可以重新開啟任何關閉連線或重新啟動頁面最初被凍結時停止的任何輪詢。
Terminated 當頁面轉變為終止狀態時,通常不需要採取任何操作。

由於使用者操作而解除安裝的頁面在進入終止狀態之前,會經過隱藏狀態,所以應該在隱藏狀態中執行會話結束邏輯(如持續的應用狀態和分析報告)。

同時(如對隱藏狀態建議中提到的那樣) ,開發人員必須認識到,在許多情況下(特別是在移動裝置上)無法精準地檢測到向終止狀態的轉換,因此依賴終止事件(如解除安裝、頁面隱藏和解除安裝)的開發人員很可能會丟失資料。
Discarded 在頁面被丟棄時,開發人員無法觀察到丟棄的狀態。 這是因為頁面通常在資源約束下被丟棄,或者是頁面解凍時,為了讓頁面能夠響應或執行,丟棄一些在大多數情況下是不可能的執行的事件。

因此,你應該要有一個心理準備,在頁面從隱藏到冷凍的狀態變更中,會有被丟棄的可能性。然後你可以通過檢查 document.wasDiscarded,在頁面負載時對丟棄頁面的恢復做出反應。


再次強調,由於在所有瀏覽器中生命週期事件的表現並不一致,所以上表中建議的最簡單方法就是使用 PageLifecycle.js

生命週期 API 的遺留問題

unload 事件

關鍵點: 在現代瀏覽器中,永遠不要使用 unload 事件.

許多開發人員將解除安裝事件視為可靠回撥,並將其作為會話結束的訊號來儲存狀態,傳送分析資料,但是這樣做是非常不可靠的,尤其是在移動端! 解除安裝事件不會在許多典型的解除安裝情況下啟動,包括關閉切換標籤或者關閉應用程式,切換程式。

出於這個原因,依賴visibilitychange事件來確定會話何時結束會更好,而且應該在隱藏狀態時,作為儲存應用程式和使用者資料的最後一個可靠時間。

此外,僅僅存在已註冊的解除安裝事件處理程式(通過 onununload 或 addEventListener ())可以阻止瀏覽器在頁面導航快取中新增頁面,以便更快地返回和轉發負載。

在所有現代瀏覽器(包括 IE11)中,建議始終使用頁面隱藏事件來檢測可能的頁面解除安裝(又稱終止狀態) ,而不是解除安裝事件。 如果您需要支援 Internet Explorer 版本10及以下版本,您應該特別檢測頁面隱藏事件,並且只在瀏覽器不支援頁面隱藏時使用解除安裝:

const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';
​
addEventListener(terminationEvent, (event) => {
  // Note: if the browser is able to cache the page, `event.persisted`
  // is `true`, and the state is frozen rather than terminated.
}, {capture: true});


有關頁面導航快取的更多資訊,以及為什麼解除安裝事件會影響頁面導航快取,請參見:

- WebKit Page Cache
- Firefox Back-Forward Cache Firefox

beforeunload 的事件

關鍵點: 永遠不要無條件的使用 beforeunload 事件,作為頁面會話結束的標誌。只有在使用者有未儲存的工作的時候,新增它。當工作被儲存後,立即移除。

beforeunload 事件與unload 事件有相似的問題,因為當它出現時,會阻止瀏覽器快取頁面

beforeunload 事件與unload 事件之間的區別在於,beforeunload 這個事件有合理的用途。 例如,當你想要警告使用者,如果他們繼續解除安裝頁面,他們將失去未儲存的更改。

由於在解除安裝之前使用是有正當場景的,但是使用它又會防止頁面被新增到頁面導航快取中,因此建議您只在使用者儲存了未儲存的更改後,才新增 beforeunload 事件監聽器,然後在儲存後立即刪除。

換句話說,不要這樣做(無條件地添加了beforeunload 事件監聽器) :

addEventListener('beforeunload', (event) => {
  // A function that returns `true` if the page has unsaved changes.
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return event.returnValue = 'Are you sure you want to exit?';
  }
}, {capture: true});


取而代之的是這樣做(因為它只在需要的時候新增 beforeunload 事件監聽器,當它不需要的時候移除它) :

const beforeUnloadListener = (event) => {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};
​
// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  addEventListener('beforeunload', beforeUnloadListener, {capture: true});
});
​
// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  removeEventListener('beforeunload', beforeUnloadListener, {capture: true});
});

注意: PageLifecycle.js 庫提供了 unsavedchanges()removeUnsavedChanges() 方法,這些方法遵循了上面的原則,稱得上是一個最佳實踐。 它們是基於一個建議草案,以一個宣告性的 API 代替以前的 unload 事件,這樣 API 不容易被濫用,在移動平臺上更加可靠。

如果您想正確使用 beforeunload 事件,並且以跨瀏覽器的方式使用, 那麼 PageLifecycle.js 庫是我們推薦的最佳解決方案。

FAQs 常見問題

我的頁面在隱藏的時候做了很重要的工作,我怎樣才能阻止它被凍結或者丟棄呢?

網頁在隱藏狀態下執行時不應該被凍結有很多合理的理由。 最明顯的例子是一個播放音樂的應用程式。

還有一些情況下,對於 Chrome 來說丟棄一個頁面是有風險的,比如如果它包含一個沒有提交的使用者輸入的表單,或者它有一個解除安裝處理程式,該處理程式會在頁面解除安裝時發出警告。

目前,Chrome 在丟棄頁面時會保守一些,只有當 Chrome 確信不會影響使用者的時候才會這樣做。 例如,在隱藏狀態下被觀察到做下列任何事情的頁面不會被丟棄,除非在極端的資源限制下:

- 播放音訊
- 使用 WebRTC
- 更新表標題或 favicon
- 顯示警告
- 傳送推送通知

注意: 對於更新標題或 favicon 以提醒使用者未讀通知的頁面,我們目前有一個提議, 從service worker那裡獲得這些更新。這將允許 Chrome 凍結或丟棄頁面,但仍然顯示標籤標題或 favicon 的更改。

頁面導航快取是什麼
頁面導航快取是一個通用術語,用來描述一些瀏覽器實現的導航優化,使得使用前後按鈕的速度更快。Webkit 稱之為”頁面快取”,火狐稱之為”後期快取”(簡稱 bfcache)。

當用戶從頁面上移開時,這些瀏覽器會凍結該頁面的版本,以便在使用者使用後向或前進按鈕的情況下迅速恢復該頁面。 請記住,新增前解除安裝或解除安裝事件處理程式,可能會阻止此優化。

出於各種目的,這種凍結功能與凍結瀏覽器的行為來保護 cpu/電池的功能相同; 因此,它被認為是生命週期狀態的一部分 — 凍結。

為什麼沒有提到載入或者 DOMContentLoaded 事件
頁面生命週期 API 定義的狀態是離散的和互斥的。 由於頁面可以被載入到活動狀態、被動狀態或隱藏狀態,所以單獨的載入狀態是沒有意義的,因為載入和 DOMContentLoaded事件不會發出生命週期狀態變化的訊號,所以它們與這個 API 無關。

如果我不能在被凍結或終止的狀態中執行非同步API,我如何將資料儲存到 IndexedDB
在凍結狀態和終止狀態中,頁面任務佇列中可被暫停,所以不能可靠地使用非同步和基於 callbackAPI,如 IndexedDB

在未來,我們將在 IDBTransaction 物件中新增一個 commit() 方法,這將給開發人員提供一種方法來執行那些不需要回調的只寫事務。 換句話說,如果開發人員只是向 IndexedDB 編寫資料,而不執行讀和寫組成的複雜事務,commit() 方法將能夠在任務佇列暫停之前的任務完成(假設 IndexedDB 資料庫已經開啟)。

然而,對如今目前的工作,開發人員有兩個選擇:
- 使用會話儲存: 會話儲存是同步的,並且是跨頁丟棄是永久的
- service worker 中使用 IndexedDB: 在頁面被終止和被拋棄之後,service worker 仍然還可以用來儲存 IndexedDB 的資料。 在凍結和頁面隱藏事件的監聽器中,你可以通過 postMessage() 來發送資料到service workerservice worker 可以儲存資料。

注意: 儘管上面的第二個選項可行,但在裝置因為記憶體壓力而凍結或丟棄頁面的情況下,瀏覽器卻不得不喚醒 service worker 的程序,這將給系統帶來更大的壓力。

在被凍結和丟棄的狀態下測試你的應用程式

為了測試你的應用程式在被凍結和丟棄的狀態下的表現,你可以訪問 chrome://discard 來操作凍結或丟棄任何開啟的標籤。


Chrome Discards UI

通過判斷 document.wasDiscarded 丟棄後,重新載入頁面。這使您可以確保您的頁面正確地處理凍結和恢復事件和文件。

總結


開發者如果想要尊重使用者裝置的系統資源,就應該在腦海中用頁面生命週期狀態來構建他們的應用程式。 網頁不能在使用者意想不到的情況下過多的消耗系統資源,這一點至關重要。

此外,越來越多的開發者開始補充新的頁面生命週期 APIS,瀏覽器凍結和丟棄那些沒有被使用的頁面就越安全。 這意味著瀏覽器將消耗更少的記憶體、 CPU、電池和網路資源,這對使用者來說是一件值得慶祝的事。

最後,開發人員如果想要實現本文描述的頁面週期變化,但又不想記住所有可能的狀態和事件轉換過程,可以使用 PageLifecycle.js 來輕鬆地觀察所有瀏覽器的生命週期狀態變化。