Interfejs Page Lifecycle API

Obsługa przeglądarek

  • Chrome: 68.
  • Edge: 79.
  • Firefox: nieobsługiwane.
  • Safari: nieobsługiwane.

Nowoczesne przeglądarki czasami zawieszają strony lub całkowicie je odrzucają, gdy zasoby systemowe są ograniczone. W przyszłości przeglądarki będą to robić w sposób proaktywny, aby zużywać mniej energii i pamięci. Interfejs API cyklu życia strony udostępnia uchwyty cyklu życia, dzięki którym strony mogą bezpiecznie obsługiwać interwencje przeglądarki bez wpływu na komfort użytkownika. Zapoznaj się z interfejsem API, aby sprawdzić, czy warto wdrożyć te funkcje w aplikacji.

Tło

Cykl życia aplikacji to kluczowy sposób zarządzania zasobami przez nowoczesne systemy operacyjne. Na Androidzie, iOS i w najnowszych wersjach Windowsa aplikacje mogą być uruchamiane i zatrzymywane w dowolnym momencie przez system operacyjny. Dzięki temu platformy mogą usprawnić działanie i przeznaczać zasoby tam, gdzie przyniosą one użytkownikom największe korzyści.

W internecie nie było do tej pory takiego cyklu życia, a aplikacje mogą działać w nieskończoność. W przypadku dużej liczby stron internetowych może dojść do nadmiernego wykorzystania ważnych zasobów systemu, takich jak pamięć, procesor, bateria i sieć, co może negatywnie wpłynąć na wrażenia użytkowników.

Platforma internetowa od dawna zawierała zdarzenia związane ze stanami cyklu życia, takie jak load, unload i visibilitychange. Jednak te zdarzenia umożliwiają deweloperom reagowanie tylko na zmiany stanu cyklu życia zainicjowane przez użytkownika. Aby internet działał niezawodnie na urządzeniach o małej mocy (i ogólnie był bardziej oszczędny pod względem zasobów), przeglądarki muszą mieć możliwość proaktywnego odzyskiwania i przeznaczania zasobów systemowych.

W rzeczywistości przeglądarki już teraz podejmują aktywne działania w celu oszczędzania zasobów na stronach na kartach w tle, a wiele przeglądarek (zwłaszcza Chrome) chciałoby robić to jeszcze bardziej, aby zmniejszyć ogólne zużycie zasobów.

Problem polega na tym, że deweloperzy nie mają możliwości przygotowania się na tego typu interwencje inicjowane przez system ani nawet nie wiedzą, że mają one miejsce. Oznacza to, że przeglądarki muszą być zachowawcze, aby nie uszkodzić stron internetowych.

Interfejs Page Lifecycle API próbuje rozwiązać ten problem, wykonując te czynności:

  • Wprowadzenie i standaryzacja koncepcji stanów cyklu życia w internecie.
  • Definiowanie nowych stanów inicjowanych przez system, które umożliwiają przeglądarkom ograniczanie zasobów, z których mogą korzystać ukryte lub nieaktywne karty.
  • Tworzenie nowych interfejsów API i zdarzeń, które umożliwiają deweloperom stron internetowych reagowanie na przejścia do i z tych nowych stanów inicjowanych przez system.

To rozwiązanie zapewnia przewidywalność, której deweloperzy potrzebują do tworzenia aplikacji odpornych na interwencje systemowe, i umożliwia przeglądarkom bardziej agresywne optymalizowanie zasobów systemowych, co w efekcie przynosi korzyści wszystkim użytkownikom.

W dalszej części tego artykułu przedstawimy nowe funkcje cyklu życia strony i omówimy ich związek ze wszystkimi dotychczasowymi stanami i zdarzeniami platformy internetowej. Zawiera on również rekomendacje i sprawdzone metody dotyczące rodzajów prac, które deweloperzy powinni (lub nie powinni) wykonywać w każdym stanie.

Omówienie stanów i zdarzeń cyklu życia strony

Wszystkie stany cyklu życia strony są oddzielne i wykluczają się wzajemnie, co oznacza, że strona może znajdować się tylko w jednym stanie w danym momencie. Większość zmian stanu cyklu życia strony można zaobserwować za pomocą zdarzeń DOM (wyjątki znajdziesz w zaleceniach dla programistów dotyczących poszczególnych stanów).

Najłatwiej wyjaśnić stany cyklu życia strony oraz zdarzenia sygnalizujące przejścia między nimi za pomocą diagramu:

Wizualizacja stanu i przepływu zdarzeń opisanych w tym dokumencie.
Stan interfejsu API cyklu życia strony i przepływ zdarzeń

Stany

Tabela poniżej zawiera szczegółowe informacje o każdym stanie. Zawiera ona też listę możliwych stanów, które mogą wystąpić przed i po zdarzeniu, a także zdarzenia, których deweloperzy mogą używać do obserwowania zmian.

Stan Opis
Aktywne

Strona jest w stanie aktywny, jeśli jest widoczna i ma fokus wprowadzania danych.

Możliwe stany poprzednie:
pasywny (poprzez zdarzenie focus)
zamrożony (poprzez zdarzenie resume, a następnie zdarzenie pageshow)

Możliwe następne stany:
pasywny (za pomocą zdarzenia blur)

Bierni odbiorcy

Strona jest w stanie pasywnym, jeśli jest widoczna i nie ma aktywnego pola wprowadzania danych.

Możliwe stany poprzednie:
aktywny (poprzez zdarzenie blur)
ukryty (poprzez zdarzenie visibilitychange)
zamrożony (poprzez zdarzenie resume, a następnie zdarzenie pageshow)

Możliwe następne stany:
active (za pomocą zdarzenia focus)
hidden (za pomocą zdarzenia visibilitychange)

Ukryta

Strona jest w stanie ukrytej, jeśli nie jest widoczna (i nie została zamrożona, odrzucona ani zakończona).

Możliwe stany poprzednie:
pasywny (przez zdarzenie visibilitychange)
zamrożony (przez zdarzenie resume, a następnie zdarzenie pageshow)

Możliwe następne stany:
pasywny (za pomocą zdarzenia visibilitychange)
zamrożony (za pomocą zdarzenia freeze)
odrzucony (nie są wywoływane żadne zdarzenia)
zakończony (nie są wywoływane żadne zdarzenia)

Zablokowany

W stanie zamrożonej przeglądarka wstrzymuje wykonywanie zadań możliwych do zamrożenia kolejkach zadań strony do momentu odmrożenia strony. Oznacza to, że nie działają między innymi liczniki JavaScriptu ani wywołania zwrotne funkcji pobierania. Zadania, które są już wykonywane, mogą się zakończyć (przede wszystkim wywołanie freeze), ale mogą być ograniczone pod względem tego, co mogą robić i jak długo mogą działać.

Przeglądarki zamrażają strony, aby oszczędzać procesor, baterię i dane. Robią to też, aby umożliwić szybsze przechodzenie do poprzedniej i następnej strony bez konieczności jej pełnego wczytywania.

Możliwe stany poprzednie:
ukryty (za pomocą zdarzenia freeze)

Możliwe następne stany:
aktywny (poprzez zdarzenie resume, a potem zdarzenie pageshow)
pasywny (poprzez zdarzenie resume, a potem zdarzenie pageshow)
ukryty (poprzez zdarzenie resume)
odrzucony (nie wygenerowano żadnych zdarzeń)

Zakończona

Gdy przeglądarka zacznie wyładowywać stronę i usuwać ją z pamięci, strona będzie w stanie zakończona. W tym stanie nie można uruchamiać nowych zadań, a trwająco wykonywane zadania mogą zostać przerwane, jeśli trwają zbyt długo.

Możliwe stany poprzednie:
ukryty (za pomocą zdarzenia pagehide)

Możliwe następne stany:
NONE

Odrzucona

Gdy przeglądarka wyładuje stronę, aby oszczędzać zasoby, strona jest w stanie odrzucona. W tym stanie nie mogą być wykonywane żadne zadania, wywołania zdarzeń ani kod JavaScript, ponieważ odrzucenia występują zwykle w warunkach ograniczonych zasobów, w których niemożliwe jest uruchamianie nowych procesów.

W stanie odrzuconej karta (w tym jej tytuł i ikona) jest zwykle widoczna dla użytkownika, nawet jeśli strona została zamknięta.

Możliwe stany poprzednie:
ukryty (nie wygenerowano żadnych zdarzeń)
zamrożony (nie wygenerowano żadnych zdarzeń)

Możliwe następne stany:
NONE

Wydarzenia

Przeglądarki wysyłają wiele zdarzeń, ale tylko niewielka ich część sygnalizuje możliwą zmianę stanu cyklu życia strony. Tabela poniżej zawiera wszystkie zdarzenia związane z cyklem życia oraz stany, między którymi mogą one występować.

Nazwa Szczegóły
focus

element DOM został zaznaczony;

Uwaga: zdarzenie focus niekoniecznie sygnalizuje zmianę stanu. sygnalizuje zmianę stanu tylko wtedy, gdy strona nie miała wcześniej fokusa na polu wprowadzania danych.

Możliwe stany poprzednie:
passive

Możliwe stany bieżące:
active

blur

Element DOM utracił fokus.

Uwaga: zdarzenie blur niekoniecznie sygnalizuje zmianę stanu. sygnalizuje zmianę stanu tylko wtedy, gdy strona nie ma już fokusa na polu wprowadzania danych (czyli nie nastąpiła zmiana fokusa z jednego elementu na inny).

Możliwe stany poprzednie:
active

Możliwe stany bieżące:
passive

visibilitychange

Zmieniła się wartość atrybutu visibilityState dokumentu. Może się to zdarzyć, gdy użytkownik przejdzie na nową stronę, przełączy karty, zamknie kartę, zminimalizuje lub zamknie przeglądarkę albo przełączy aplikacje w systemie operacyjnym urządzenia mobilnego.

Możliwe stany poprzednie:
passive
hidden

Możliwe stany bieżące:
pasywny
ukryty

freeze *

Strona została właśnie zablokowana. Nie rozpocznie się żadne zadanie do zamrożenia w kole zadania strony.

Możliwe stany poprzednie:
ukryty

Możliwe bieżące stany:
zamrożony

resume *

przeglądarka wznowiła zablokowaną stronę;

Możliwe stany poprzednie:
zamrożony

Możliwe stany bieżące:
aktywny (jeśli po nim następuje zdarzenie pageshow)
pasywny (jeśli po nim następuje zdarzenie pageshow)
ukryty

pageshow

Przechodzimy do wpisu z historii sesji.

Może to być zupełnie nowa strona lub strona pobrana z pamięci podręcznej. Jeśli strona została pobrana z pamięci podręcznej stanu strony internetowej, właściwość persisted zdarzenia ma wartość true, w przeciwnym razie – wartość false.

Możliwe stany poprzednie:
zamrożony (wystąpiło też zdarzenie resume)

Możliwe stany:
aktywny
pasywny
ukryty

pagehide

Przechodzimy z poziomu wpisu z historii sesji.

Jeśli użytkownik przechodzi na inną stronę, a przeglądarka może dodać bieżącą stronę do pamięci podręcznej wstecz/wprzód, aby można było jej użyć później, właściwość persisted zdarzenia ma wartość true. Jeśli true, strona przechodzi w stan zamrożony, w przeciwnym razie przechodzi w stan zakończony.

Możliwe stany poprzednie:
ukryty

Możliwe bieżące stany:
zamrożone (event.persisted to prawda, freeze następuje zdarzenie)
zakończone (event.persisted to fałsz, unload następuje zdarzenie)

beforeunload

Okno, dokument i jego zasoby zostaną usunięte z pamięci. Dokument jest nadal widoczny, a wydarzenie można anulować.

Ważne: zdarzenie beforeunload należy używać tylko do powiadamiania użytkownika o niezapisanych zmianach. Gdy zapiszesz te zmiany, wydarzenie powinno zostać usunięte. Nie należy ich bezwarunkowo dodawać do strony, ponieważ w niektórych przypadkach może to pogorszyć jej skuteczność. Szczegółowe informacje znajdziesz w sekcji poświęconej starszym interfejsom API.

Możliwe stany poprzednie:
ukryty

Możliwe stany:
terminated

unload

Strona jest wyładowywana.

Ostrzeżenie: używanie zdarzenia unload nie jest zalecane, ponieważ jest zawodne i w niektórych przypadkach może obniżać skuteczność. Więcej informacji znajdziesz w sekcji poświęconej starszym interfejsom API.

Możliwe stany poprzednie:
ukryty

Możliwe stany bieżące:
terminated

* Wskazuje nowe zdarzenie zdefiniowane przez interfejs Page Lifecycle API.

Nowe funkcje dodane w Chrome 68

Poprzedni wykres pokazuje 2 stany, które są wywoływane przez system, a nie przez użytkownika: zamrożoneodrzucone. Jak już wspomnieliśmy, przeglądarki obecnie już czasami zawieszają i zamykają ukryte karty (według własnego uznania), ale deweloperzy nie mają możliwości sprawdzenia, kiedy to się dzieje.

W Chrome 68 deweloperzy mogą teraz obserwować, kiedy ukryta karta jest zablokowana i odblokowana, na podstawie zdarzeń freezeresumedocument.

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

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

Od wersji 68 w Chrome na komputery obiekt document zawiera teraz właściwość wasDiscarded (w tym zgłoszeniu śledzimy obsługę Androida). Aby sprawdzić, czy strona została odrzucona, gdy karta była ukryta, możesz sprawdzić wartość tej właściwości podczas wczytywania strony (uwaga: aby ponownie użyć odrzuconej strony, musisz ją ponownie załadować).

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

Wskazówki dotyczące ważnych czynności w przypadku zdarzeń freezeresume, a także informacje o tym, jak obsługiwać i przygotowywać strony do odrzucenia, znajdziesz w rekomendacjach dla programistów dotyczących każdego stanu.

W następnych sekcjach omawiamy, jak te nowe funkcje pasują do istniejących stanów i zdarzeń platformy internetowej.

Jak obserwować stany cyklu życia strony w kodzie

W stanach active (aktywny), passive (bierny) i hidden (ukryty) można uruchamiać kod JavaScript, który określa bieżący stan cyklu życia strony za pomocą istniejących interfejsów API platformy internetowej.

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

Stany zamrożonezakończone można natomiast wykryć tylko w odpowiednich detektorówach zdarzeń (freezepagehide), ponieważ stany te się zmieniają.

Jak obserwować zmiany stanu

Korzystając z określonej wcześniej funkcji getState(), możesz obserwować wszystkie zmiany stanu cyklu życia strony za pomocą tego kodu.

// 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;
  }
};

// Options used for all event listeners.
const opts = {capture: true};

// 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()), opts);
});

// 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');
}, opts);

window.addEventListener('pagehide', (event) => {
  // If the event's persisted property is `true` the page is about
  // to enter the back/forward cache, which is also in the frozen state.
  // If the event's persisted property is not `true` the page is
  // about to be unloaded.
  logStateChange(event.persisted ? 'frozen' : 'terminated');
}, opts);

Ten kod wykonuje 3 działania:

  • Ustawia stan początkowy za pomocą funkcji getState().
  • Definiuje funkcję, która przyjmuje następny stan i w razie zmiany rejestruje zmiany stanu w konsoli.
  • Dodaje detektory zdarzeń dla wszystkich niezbędnych zdarzeń cyklu życia, które z kolei wywołują logStateChange(), przekazując następny stan.

Warto zwrócić uwagę, że wszystkie detektory zdarzeń są dodawane do window i wszystkie są przekazywane do {capture: true}. Dzieje się tak z kilku przyczyn:

  • Nie wszystkie zdarzenia z cyklu życia strony mają ten sam cel. pagehidepageshow są wywoływane w elementach window; visibilitychange, freezeresume są wywoływane w elementach document, a focusblur są wywoływane w odpowiednich elementach DOM.
  • Większość z nich nie powoduje przenoszenia zdarzeń, co oznacza, że nie można dodać do wspólnego elementu przodka detektorów zdarzeń nieprzechwytujących i obserwować wszystkich zdarzeń.
  • Faza przechwytywania jest wykonywana przed fazami docelową i bąbelkową, więc dodanie tam słuchaczy pomaga zapewnić, aby były one wykonywane przed anulowaniem przez inny kod.

Zalecenia dla deweloperów w przypadku każdego stanu

Jako programiści powinniście znać stany cyklu życia strony oraz wiedzieć, jak je sprawdzać w kodzie, ponieważ rodzaj działań, które należy wykonywać (lub nie), zależy w dużej mierze od stanu strony.

Nie ma sensu wyświetlanie użytkownikowi powiadomienia przejściowego, jeśli strona jest ukryta. Ten przykład jest dość oczywisty, ale warto wymienić inne, mniej oczywiste rekomendacje.

Stan Rekomendacje dla deweloperów
Active

Stan aktywny jest najważniejszy dla użytkownika, a więc strona powinna reagować na jego działania.

Wszelkie zadania niezwiązane z interfejsem użytkownika, które mogą blokować wątek główny, powinny być odprawiane na dalszy plan podczas okresów bezczynności lub przekazywane do wątku internetowego.

Passive

W stanie pasywnym użytkownik nie wchodzi w interakcję ze stroną, ale nadal ją widzi. Oznacza to, że animacje i aktualizacje interfejsu powinny przebiegać płynnie, ale czas ich trwania nie jest tak istotny.

Gdy strona zmieni stan z aktywnej na bierną, warto zapisać niezapisane stany aplikacji.

Hidden

Gdy strona zmieni stan z biernego na ukryty, użytkownik może nie wchodzić z nią w interakcję, dopóki nie zostanie ona ponownie załadowana.

Przejście do stanu ukryte jest też często ostatnią zmianą stanu, którą deweloperzy mogą niezawodnie zaobserwować (szczególnie na urządzeniach mobilnych, ponieważ użytkownicy mogą zamykać karty lub całą aplikację przeglądarki, a w takich przypadkach nie są wywoływane zdarzenia beforeunload, pagehideunload).

Oznacza to, że stan ukryty należy traktować jako prawdopodobne zakończenie sesji użytkownika. Inaczej mówiąc, zachować niezapisane stany aplikacji i wysłać niezasłane dane analityczne.

Powinieneś też przestać wprowadzać zmiany w interfejsie (ponieważ użytkownik ich nie widzi) i zatrzymać wszystkie zadania, których użytkownik nie chce wykonywać w tle.

Frozen

W stanie zamrożonej strony zadania podlegające zamrożeniu kolejkach zadań są zawieszone do czasu odblokowania strony, co może nigdy nie nastąpić (np. jeśli strona zostanie odrzucona).

Oznacza to, że gdy strona zmieni stan z ukrytej na zamrożoną, konieczne jest zatrzymanie wszystkich zegarów lub zerwanie połączeń, które w przypadku zamrożenia mogłyby wpłynąć na inne otwarte karty w tej samej domenie lub na możliwość umieszczenia strony w  pamięci podręcznej przeglądarki.

W szczególności ważne jest, aby:

Musisz też zachować stan dynamicznego widoku (np. pozycję przewijania w nieskończonej liście) w  sessionStorage (lub w IndexedDB za pomocą commit()), który chcesz przywrócić, jeśli strona zostanie odrzucona i ponownie wczytana.

Jeśli strona zmieni stan z zamrożonego na ukryty, możesz ponownie otworzyć zamknięte połączenia lub wznowić przerwane wywołania, które zostały zatrzymane, gdy strona była zamrożona.

Terminated

Zazwyczaj nie musisz podejmować żadnych działań, gdy strona przechodzi do stanu zakończona.

Strony, które są wyładowywane w wyniku działania użytkownika, zawsze przechodzą przez stan ukryty, zanim staną się stanem zakończony.W stanie ukrytym powinna być wykonywana logika kończenia sesji (np. zapisywanie stanu aplikacji i raportowanie do Analytics).

Ponadto (jak wspomniano w zaleceniach dotyczących stanu ukryty), deweloperzy powinni pamiętać, że w wielu przypadkach (zwłaszcza na urządzeniach mobilnych) nie można niezawodnie wykryć przejścia do stanu zakończony, dlatego deweloperzy, którzy polegają na zdarzeniach zakończenia (np. beforeunload, pagehideunload), prawdopodobnie tracą dane.

Discarded

Deweloperzy nie mogą zauważyć stanu wyrzucony w momencie, gdy strona zostaje wyrzucona. Dzieje się tak, ponieważ strony są zazwyczaj odrzucane z powodu ograniczeń zasobów, a odblokowanie strony tylko po to, aby umożliwić uruchomienie skryptu w odpowiedzi na zdarzenie odrzucenia, w większości przypadków jest po prostu niemożliwe.

W związku z tym musisz się przygotować na możliwość odrzucenia zmiany z ukrytej na zamrożoną. Następnie możesz zareagować na przywrócenie odrzuconej strony podczas jej wczytywania, zaznaczając document.wasDiscarded.

Ponieważ niezawodność i kolejność zdarzeń cyklu życia nie są konsekwentnie wdrażane we wszystkich przeglądarkach, najłatwiej jest postępować zgodnie z zaleceniami w tabeli, używając pliku PageLifecycle.js.

Starszych interfejsów API cyklu życia, których należy unikać

Należy unikać tych zdarzeń, jeśli to możliwe.

Zdarzenie wyładowania

Wielu deweloperów traktuje zdarzenie unload jako gwarantowany wywołanie zwrotne i używa go jako sygnału zakończenia sesji w celu zapisania stanu i wysłania danych analitycznych, ale jest to bardzo niepewne, zwłaszcza na urządzeniach mobilnych. Zdarzenie unload nie jest wywoływane w wielu typowych sytuacjach rozładowania, np. podczas zamykania karty w przełączniku kart na urządzeniu mobilnym lub zamykania aplikacji przeglądarki w przełączniku aplikacji.

Dlatego zawsze lepiej jest polegać na zdarzeniu visibilitychange, aby określić, kiedy kończy się sesja, i uznać stan ukryty za ostatni niezawodny moment zapisywania danych aplikacji i użytkowników.

Co więcej, sama obecność zarejestrowanego modułu obsługi zdarzenia unload (za pomocą onunload lub addEventListener()) może uniemożliwiać przeglądarkom umieszczanie stron w pamięci podręcznej stanu strony internetowej w celu szybszego wczytywania stron wstecz i do przodu.

We wszystkich nowoczesnych przeglądarkach zalecamy zawsze używanie zdarzenia pagehide do wykrywania możliwych zwolnień pamięci strony (czyli stanu zakończony) zamiast zdarzenia unload. Jeśli chcesz obsługiwać Internet Explorera w wersji 10 lub starszej, musisz wykryć zdarzenie pagehide i użyć funkcji unload tylko wtedy, gdy przeglądarka nie obsługuje:pagehide

const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';

window.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.
});

Zdarzenie beforeunload

Zdarzenie beforeunload ma podobny problem do zdarzenia unload, ponieważ w przeszłości obecność zdarzenia beforeunload mogła uniemożliwić stronom kwalifikowanie się do korzystania z pamięci podręcznej stanu strony internetowej. Nowoczesne przeglądarki nie mają tego ograniczenia. Niektóre przeglądarki nie wywołują zdarzenia beforeunload, gdy próbują umieścić stronę w pamięci podręcznej wstecz/wstecz, co oznacza, że zdarzenie to nie jest wiarygodnym sygnałem końca sesji. Dodatkowo niektóre przeglądarki (w tym Chrome) wymagają interakcji użytkownika ze stroną, zanim pozwolą na wywołanie zdarzenia beforeunload, co dodatkowo wpływa na niezawodność tego zdarzenia.

Jedną z różnic między beforeunloadunload jest to, że beforeunload może być używany w legalnych celach. Możesz na przykład ostrzec użytkownika, że jeśli będzie kontynuować wylogowywanie, utraci niezapisane zmiany.

Ponieważ istnieją uzasadnione powody, dla których warto używać beforeunload, zalecamy tylko dodawanie odbiorców beforeunload, gdy użytkownik ma niezapisane zmiany, a następnie usuwanie ich natychmiast po zapisaniu.

Inaczej mówiąc, nie rób tego (ponieważ bezwarunkowo dodajesz listenera beforeunload):

addEventListener('beforeunload', (event) => {
  // A function that returns `true` if the page has unsaved changes.
  if (pageHasUnsavedChanges()) {
    event.preventDefault();

    // Legacy support for older browsers.
    return (event.returnValue = true);
  }
});

Zamiast tego użyj tego kodu (ponieważ dodaje on parametr beforeunload listener tylko wtedy, gdy jest to potrzebne, a usuwanie go, gdy nie jest to konieczne):

const beforeUnloadListener = (event) => {
  event.preventDefault();
  
  // Legacy support for older browsers.
  return (event.returnValue = true);
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  removeEventListener('beforeunload', beforeUnloadListener);
});

Najczęstsze pytania

Dlaczego nie ma stanu „Ładowanie”?

Interfejs Page Lifecycle API definiuje stany jako oddzielne i wzajemnie się wykluczające. Strona może być wczytana w stanie aktywnym, biernym lub ukrytym, a przed zakończeniem wczytywania może zmienić stan lub zostać zamknięta. W ramach tej zasady oddzielny stan wczytywania nie ma sensu.

Moja strona wykonuje ważne zadania, gdy jest ukryta. Jak mogę zapobiec jej zamrożeniu lub odrzuceniu?

Istnieje wiele uzasadnionych powodów, dla których strony internetowe nie powinny być zamrażane podczas działania w stanie ukrytym. Najbardziej oczywistym przykładem jest aplikacja odtwarzająca muzykę.

Są też sytuacje, w których odrzucenie strony przez Chrome byłoby ryzykowne, na przykład gdy zawiera ona formularz z nieprzesłanymi danymi użytkownika lub gdy ma element beforeunload, który ostrzega o usuwaniu strony.

Na razie Chrome będzie ostrożnie odrzucać strony i robić to tylko wtedy, gdy będzie mieć pewność, że nie wpłynie to na użytkowników. Na przykład strony, które w stanie ukrytym wykonują którąkolwiek z tych czynności, nie zostaną odrzucone, chyba że występują skrajne ograniczenia zasobów:

  • Odtwarzanie dźwięku
  • Korzystanie z WebRTC
  • Aktualizowanie tytułu tabeli lub jej ikony
  • Wyświetlanie alertów
  • Wysyłanie powiadomień push

Informacje o obecnych funkcjach listy, które służą do określania, czy kartę można bezpiecznie zamrozić lub odrzucić, znajdziesz w artykule Heurystyka zamrażania i odrzucania kart w Chrome.

Co to jest pamięć podręczna stanu strony internetowej?

Pamięć podręczna stanu strony internetowej to termin używany do opisania optymalizacji nawigacji, którą niektóre przeglądarki implementują, aby przyspieszyć korzystanie z przycisków wstecz i dalej.

Gdy użytkownik opuszcza stronę, te przeglądarki zamrażają jej wersję, aby można było szybko do niej wrócić, jeśli użytkownik przejdzie do niej za pomocą przycisków Wstecz lub Wprzód. Pamiętaj, że dodanie unloadobsługi zdarzeń uniemożliwia tę optymalizację.

W praktyce zamrażanie jest takie samo jak zamrażanie przeglądarek w celu oszczędzania procesora lub baterii. Z tego powodu jest to uważane za część stanu cyklu życia zamrożonego.

Jeśli nie mogę uruchomić interfejsów API asynchronicznych w zamrożonym lub zakończonym stanie, jak mogę zapisać dane w IndexedDB?

W stanach zamrożonego i zakończonego zadania zamrożalnekole zadań strony są zawieszone, co oznacza, że nie można niezawodnie używać interfejsów API asynchronicznych i opartych na wywołaniu zwrotnym, takich jak IndexedDB.

W przyszłości do obiektów IDBTransaction dodamy metodę commit(), która umożliwi deweloperom przeprowadzanie transakcji, które są w istocie transakcjami tylko do zapisu i nie wymagają wywołań zwrotnych. Innymi słowy, jeśli deweloper tylko zapisuje dane w IndexedDB i nie wykonuje złożonej transakcji polegającej na odczycie i zapisaniu danych, metoda commit() będzie mogła się zakończyć, zanim kolejki zadań zostaną zawieszone (zakładając, że baza danych IndexedDB jest już otwarta).

W przypadku kodu, który musi działać już teraz, deweloperzy mają 2 opcje:

  • Używanie pamięci sesji: pamięć sesji działa synchronicznie i zapisuje dane po odrzuceniu strony.
  • Używanie IndexedDB z serwisowego workera: serwisowy worker może przechowywać dane w IndexedDB po zakończeniu działania lub odrzuceniu strony. W słuchaczu zdarzeń freeze lub pagehide możesz wysyłać dane do serwisowego workera za pomocą postMessage(). Worker może następnie zapisać te dane.

Testowanie aplikacji w zamrożonym i odrzuconym stanie

Aby sprawdzić, jak aplikacja zachowuje się w zamrożonym i odrzuconym stanie, możesz kliknąć chrome://discards, aby zamrozić lub odrzucić dowolną otwartą kartę.

Interfejs usuwania w Chrome
Interfejs Discards w Chrome

Dzięki temu możesz mieć pewność, że Twoja strona prawidłowo obsługuje zdarzenia freezeresume, a także flagę document.wasDiscarded podczas ponownego wczytywania stron po odrzuceniu.

Podsumowanie

Programiści, którzy chcą szanować zasoby systemowe urządzeń użytkowników, powinni tworzyć aplikacje z uwzględnieniem stanów cyklu życia strony. Ważne jest, aby strony internetowe nie zużywały nadmiernych zasobów systemowych w sytuacjach, których użytkownik nie przewidział.

Im więcej deweloperów zacznie wdrażać nowe interfejsy API cyklu życia strony, tym bezpieczniej będzie dla przeglądarek zamrażać i usuwać strony, których nie używa się. Oznacza to, że przeglądarki będą zużywać mniej pamięci, procesora, baterii i zasobów sieciowych, co jest korzystne dla użytkowników.