Abstract

When we take a closer look at browser performance, we often come across questions like:

  1. When you open/switch a Tab, the page behaves differently: sometimes you can interact directly, sometimes you need to reload the page to interact, and sometimes the page freezes.
  2. When should different performance indicators be captured and reported when web application analysis data needs to be reported?
  3. .

In fact, through the browser page life cycle state management mechanism research, can help us better understand the above problem!

Background

The core of system management lies in resources

Application life cycle management is a key way for the system to manage resources. In Android, IOS, and Windows, you can start/stop an application at any time, reallocate resources, and pursue the ultimate user experience.

Previously, on the browser side, Web apps could keep running until they were passively discarded or suspended when resources were overloaded. We are also used to responding to user-induced changes in life cycle state, such as capturing onLoad, beforeUnload, Unload events when we open/switch/close a Web app. However, the browser can now actively freeze or release resources to reduce resource consumption (such as memory, CPU, battery) and improve the user experience.

The question then arises – how do we detect the actions that browsers actively perform on resources and how do we capture system-induced changes in life cycle state?

The Answer is Page Lifecycle API — Page Lifecycle provides a series of Lifecycle hooks to help us more safely intervene and take action on different browser states. Page Lifecycle addresses three main things:

  1. Introducing standardized page life cycle states and concepts;
  2. Define new, system-initiated change states that allow browsers to limit the amount of system resources consumed by tabs when they are hidden or inactive;
  3. Create new apis and events to allow developers to capture and respond to system-induced state changes;

Page Lifecycle States

Important: Page life cycle all states are discrete and independent, meaning that only one state can exist on a page at a time

The figure above covers all the possible states of the page and the events that trigger them. The key points are analyzed below:

browser-initiated

Two status changes caused by the system: FROZEN and DISCARDED

FROZEN

In the frozen state, the browser stops executing the freezable tasks in the event queue (such as timer tasks in JavaScript, request tasks, etc.) until the page is unfrozen. Tasks that have been executed can continue to be executed, but their use of resources (scope and execution time) is limited. At the same time, the browser maintains a certain amount of page CPU/ memory resource usage, allowing it to respond more quickly to browser forward/Back events (the back-froward Cache mechanism) without having to reload the entire page.

DISCARDED

When a page is completely unmounted by the browser, the page is in the deprecated state and no events are executed

If a page changes from the HIDDEN state to the FROZEN state, CPU resources are preferentially released. If a page changes from the FROZEN state to the DISCARDED state, memory resources are released.


DISCARDED vs TERMINATED

DISCARDED

  • When a page is completely uninstalled by the browser, the page becomes obsolete and the browser causes the state change
  • throughdocument.wasDiscardedTo determine whether the current page was previously discarded
  • The former state is FROZEN

TERMINATED

  • The page is terminated when it is unloaded by the browser and cleaned from memory
  • In this state, new tasks are not executed, but executed tasks are terminated by limiting resources
  • The former state is HIDDEN

Hidden State change

HIDDEN -> FROZEN There are two ways

  1. Controlled by the system, the system actively freezes pages to reduce CPU consumption by listeningfreezeCapture its state changes
  2. Triggered by user action, the user switches to another page (multi-page application) on the current TAB or closes the current TAB, Window or app. It’s going to happen in the processbeforeunload.pagehideEvent, ifpagehideEvents in theevent.persisted === true(that is, the browser determines that it can cache page resources), and freezes

HIDDEN -> TERMINATED

Triggered by user action, the user switches to another page (multi-page application) on the current TAB or closes the current TAB, Window or app. Beforeunload, pageHide events are experienced, and if the event. Persisted === true in the PageHide event (that is, the browser judges that the page resource can be cached), then the unload event will be aborted

How can Page Lifecycle changes be observed?

Talk is cheap, show me the code!

// ACTIVE, PASSIVE, HIDDEN
const D = document
const W = window
function getStates() {
  if (D.visibilityState === 'hidden') return 'hidden'
  if (D.hasFocus()) return 'active'
  return 'passive'
}

// FROZEN 1
W.addEventListener('freeze'.() = > { console.log('Current State is frozen')}, {capture: true })

// FROZEN 2, TERMINATED
W.addEventListener('pagehide'.(e) = > {
  if(e.persisted) return 'frozen'
  return 'terminated'
}, { capture: true })
Copy the code

One thing to note about the above code: All event listeners are mounted to the Window object and need to be set to {capture: true}. Why?

  • Not all page life cycle events have the same goal.pagehide.pageshowwindowObject to fire;visibilitychange.freezeAs well asresumedocumentObject to fire;focus.blurFires on the corresponding DOM element
  • Most page life cycle events do not bubble, so it is not possible to add a non-captured event listener to a common ancestor element and observe all event behavior
  • The capture phase is executed before the target or bubbling phase, so adding listeners to that phase ensures that it will not be canceled by other code at that time

Perform the correct actions in different Page Lifecycle states

Here are some development recommendations for core states and events

Hidden

Hidden is usually the end state of the user’s session

  • Listening for this state is reliable because on mobile applications, the user closes the TAB or the browser itself, which is not triggered in this casebeforeunload.pagehideunloadThe event
  • 1. UI update; 2. Perform background tasks
  • Suggestion: 1. Save all unsaved application states. 2. Send unsent analysis data.

Frozen

  • Stopping a Timer Task
  • 1. IndexedDB connection; 2. BroadcastChannel connection. 3. WebRTC connection; 4. Network polling and Web Socket connection;

unload

Many materials use the Unload event as a reliable indicator of the end of a page session, and use its callbacks to store state and send analysis data. However, this behavior is extremely unreliable on mobile phones. A better way is to monitor visibilitychange.

Even on the browser side, it is recommended to listen for pageHide events in response to page termination status


beforeunload

Beforeunload comes in handy when you want to warn the user that unsaved changes on the current page will disappear if you continue to unload the page!

However, beforeUnload, like the unload event, breaks the browser’s back-froward Cache mechanism, so it is recommended to add beforeunlaod listeners only when unsaved changes are prompted, and uninstall them as soon as the user saves!

const beforeUnloadListener = (event) = > {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit? ';
};

// When there are unsaved changes to the page
onPageHasUnsavedChanges(() = > {
  addEventListener('beforeunload', beforeUnloadListener, {capture: true});
});

// When the page has been saved
onAllChangesSaved(() = > {
  removeEventListener('beforeunload', beforeUnloadListener, {capture: true});
});
Copy the code

Look at Page Lifecycle States in your browser

Chrome :// Discards allows you to view Tab status information in your current browser.

🔥 Solo with code! 🔥