Interfejs Page Lifecycle API

Browser Support

  • Chrome: 68.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Współczesne przeglądarki czasami wstrzymują lub całkowicie odrzucają strony, gdy zasoby systemowe są ograniczone. W przyszłości przeglądarki będą to robić proaktywnie, aby zużywać mniej energii i pamięci. Interfejs Page Lifecycle API udostępnia punkty zaczepienia cyklu życia, dzięki czemu strony mogą bezpiecznie obsługiwać te interwencje przeglądarki bez wpływu na wrażenia użytkowników. Zapoznaj się z interfejsem API, aby sprawdzić, czy warto wdrożyć te funkcje w aplikacji.

Tło

Cykl życia aplikacji to kluczowy sposób, w jaki nowoczesne systemy operacyjne zarządzają zasobami. W systemach Android, iOS i najnowszych wersjach Windows aplikacje mogą być w każdej chwili uruchamiane i zatrzymywane przez system operacyjny. Dzięki temu platformy te mogą usprawniać i przekierowywać zasoby tam, gdzie przynoszą użytkownikom największe korzyści.

W internecie nie było dotychczas takiego cyklu życia, a aplikacje można było utrzymywać w stanie aktywnym w nieskończoność. Przy dużej liczbie uruchomionych stron internetowych kluczowe zasoby systemowe, takie jak pamięć, procesor, bateria i sieć, mogą być nadmiernie wykorzystywane, co negatywnie wpływa na wrażenia użytkowników.

Platforma internetowa od dawna ma zdarzenia związane ze stanami cyklu życia, takie jak load, unloadvisibilitychange. Umożliwiają one jednak programistom reagowanie tylko na zmiany stanu cyklu życia zainicjowane przez użytkownika. Aby internet działał niezawodnie na urządzeniach o niskiej mocy (i był bardziej oszczędny w zakresie zasobów na wszystkich platformach), przeglądarki potrzebują sposobu na proaktywne odzyskiwanie i ponowne przydzielanie zasobów systemowych.

Obecnie przeglądarki podejmują aktywne działania w celu oszczędzania zasobów w przypadku stron na kartach w tle, a wiele przeglądarek (zwłaszcza Chrome) chciałoby robić to w większym stopniu, 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ć ostrożne, aby nie uszkodzić stron internetowych.

Interfejs Page Lifecycle API próbuje rozwiązać ten problem w ten sposób:

  • Wprowadzenie i ujednolicenie koncepcji stanów cyklu życia w internecie.
  • Określanie nowych stanów inicjowanych przez system, które umożliwiają przeglądarkom ograniczanie zasobów, które mogą być wykorzystywane przez 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 programistom internetowym przewidywalność, której potrzebują do tworzenia aplikacji odpornych na interwencje systemowe, a przeglądarkom umożliwia bardziej agresywną optymalizację zasobów systemowych, co ostatecznie przynosi korzyści wszystkim użytkownikom internetu.

W dalszej części tego posta przedstawimy nowe funkcje cyklu życia strony i wyjaśnimy, jak odnoszą się one do wszystkich istniejących stanów i zdarzeń platformy internetowej. Zawiera też rekomendacje i sprawdzone metody dotyczące rodzajów prac, które deweloperzy powinni (i nie powinni) wykonywać w poszczególnych stanach.

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

Wszystkie stany cyklu życia strony są odrębne i wzajemnie się wykluczają, co oznacza, że strona może znajdować się tylko w jednym stanie naraz. Większość zmian stanu cyklu życia strony jest zwykle widoczna za pomocą zdarzeń DOM (wyjątki znajdziesz w rekomendacjach dla programistów dotyczących każdego stanu).

Najprostszym sposobem wyjaśnienia stanów cyklu życia strony oraz zdarzeń, które sygnalizują przejścia między nimi, jest diagram:

Wizualna reprezentacja stanu i przepływu zdarzeń opisanych w tym dokumencie.
Stan interfejsu Page Lifecycle API i przepływ zdarzeń

Stany

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

Stan Opis
Aktywne

Strona jest w stanie aktywnym, jeśli jest widoczna i ma fokus wejściowy.

Możliwe poprzednie stany:
passive (za pomocą zdarzenia focus)
frozen (za pomocą zdarzenia resume, a następnie zdarzenia pageshow)

Możliwe kolejne stany:
passive (za pomocą zdarzenia blur)

Pasywny

Strona jest w stanie pasywnym, jeśli jest widoczna i nie jest aktywna.

Możliwe poprzednie stany:
aktywny (za pomocą zdarzenia blur)
ukryty (za pomocą zdarzenia visibilitychange)
zamrożony (za pomocą zdarzenia resume, a następnie zdarzenia pageshow)

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

Ukryta

Strona jest w stanie ukrytym, jeśli nie jest widoczna (i nie została zamrożona, odrzucona ani zamknięta).

Możliwe poprzednie stany:
passive (za pomocą zdarzenia visibilitychange)
frozen (za pomocą zdarzenia resume, a następnie zdarzenia pageshow)

Możliwe kolejne stany:
passive (za pomocą zdarzenia visibilitychange)
frozen (za pomocą zdarzenia freeze)
discarded (nie wywołano żadnych zdarzeń)
terminated (nie wywołano żadnych zdarzeń)

Zablokowane

W stanie zamrożonym przeglądarka wstrzymuje wykonywanie zadań w kolejkach zadań na stronie, które można zamrozić, dopóki strona nie zostanie odmrożona. Oznacza to, że nie działają takie elementy jak timery JavaScriptu i wywołania zwrotne pobierania. Trwające zadania mogą zostać ukończone (przede wszystkim wywołanie zwrotne freeze), ale mogą mieć ograniczone możliwości i czas działania.

Przeglądarki zamrażają strony, aby oszczędzać procesor, baterię i dane. Robią to też, aby umożliwić szybsze przechodzenie do przodu i do tyłu – unikając konieczności ponownego załadowania całej strony.

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

Możliwe kolejne stany:
aktywny (po zdarzeniu resume, a następnie zdarzeniu pageshow)
pasywny (po zdarzeniu resume, a następnie zdarzeniu pageshow)
ukryty (po zdarzeniu resume)
odrzucony (bez wywoływania zdarzeń)

Zakończono

Strona jest w stanie zakończonym, gdy przeglądarka zacznie ją zwalniać i usuwać z pamięci. Nie można rozpocząć nowych zadań w tym stanie, a trwające zadania mogą zostać przerwane, jeśli będą wykonywane zbyt długo.

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

Możliwe kolejne stany:
BRAK

Odrzucono

Strona jest w stanie odrzuconym, gdy przeglądarka wyładuje ją z pamięci, aby zaoszczędzić zasoby. W tym stanie nie można uruchamiać żadnych zadań, wywołań zwrotnych zdarzeń ani kodu JavaScript, ponieważ odrzucanie zwykle następuje w przypadku ograniczeń zasobów, gdy rozpoczęcie nowych procesów jest niemożliwe.

W stanie odrzuconym karta (wraz z tytułem i ikoną) jest zwykle widoczna dla użytkownika, mimo że strona już nie istnieje.

Możliwe poprzednie stany:
hidden (nie wywołano żadnych zdarzeń)
frozen (nie wywołano żadnych zdarzeń)

Możliwe kolejne stany:
BRAK

Wydarzenia

Przeglądarki wysyłają wiele zdarzeń, ale tylko niewielka ich część sygnalizuje możliwą zmianę stanu cyklu życia strony. W tabeli poniżej znajdziesz wszystkie zdarzenia związane z cyklem życia oraz stany, do których mogą przechodzić i z których mogą wychodzić.

Nazwa Szczegóły
focus

Element DOM został zaznaczony.

Uwaga: zdarzenie focus nie musi sygnalizować zmiany stanu. Sygnalizuje zmianę stanu tylko wtedy, gdy strona nie miała wcześniej fokusu na dane wejściowe.

Możliwe poprzednie stany:
passive

Możliwe obecne stany:
aktywny

blur

Element DOM utracił fokus.

Uwaga: zdarzenie blur nie musi sygnalizować zmiany stanu. Sygnalizuje zmianę stanu tylko wtedy, gdy strona nie ma już fokusu wejściowego (tzn. nie przełączyła się z jednego elementu na inny).

Możliwe poprzednie stany:
aktywne

Możliwe stany bieżące:
passive

visibilitychange

Wartość visibilityState dokumentu uległa zmianie. 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 systemach operacyjnych urządzeń mobilnych.

Możliwe poprzednie stany:
passive
hidden

Możliwe stany bieżące:
passive
hidden

freeze *

Strona została właśnie zamrożona. Żadne zadanie, które można zamrozić w kolejkach zadań strony, nie zostanie uruchomione.

Możliwe poprzednie stany:
ukryty

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

resume *

Przeglądarka wznowiła działanie zamrożonej strony.

Możliwe poprzednie stany:
zamrożony

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

pageshow

Przechodzisz do wpisu w historii sesji.

Może to być nowe wczytanie strony lub strona pobrana z pamięci podręcznej stanu strony internetowej. 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 ma wartość false.

Możliwe poprzednie stany:
zamrożony (w tym przypadku zostałoby też wywołane zdarzenie resume)

Możliwe stany bieżące:
aktywny
pasywny
ukryty

pagehide

Wpis w historii sesji jest przeglądany.

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

Możliwe poprzednie stany:
ukryty

Możliwe stany bieżące:
zamrożony (event.persisted ma wartość true, freeze następuje zdarzenie)
zakończony (event.persisted ma wartość false, unload następuje zdarzenie)

beforeunload

Okno, dokument i jego zasoby zostaną wyładowane. Dokument jest nadal widoczny, a wydarzenie można w tym momencie anulować.

Ważne: zdarzenie beforeunload powinno być używane tylko do informowania użytkownika o niezapisanych zmianach. Po zapisaniu tych zmian wydarzenie powinno zostać usunięte. Nigdy nie należy dodawać go do strony bezwarunkowo, ponieważ w niektórych przypadkach może to pogorszyć wydajność. Szczegółowe informacje znajdziesz w sekcji dotyczącej starszych interfejsów API.

Możliwe poprzednie stany:
ukryty

Możliwe obecne stany:
terminated

unload

Strona jest zamykana.

Ostrzeżenie: używanie zdarzenia unload nigdy nie jest zalecane, ponieważ jest ono zawodne i w niektórych przypadkach może pogorszyć wydajność. Więcej informacji znajdziesz w sekcji dotyczącej starszych interfejsów API.

Możliwe poprzednie stany:
ukryty

Możliwe obecne stany:
terminated

* Wskazuje nowe zdarzenie zdefiniowane przez interfejs Page Lifecycle API.

Nowe funkcje dodane w Chrome 68

Na poprzednim wykresie widać 2 stany, które są inicjowane przez system, a nie przez użytkownika: zamrożonyodrzucony. Jak wspomnieliśmy wcześniej, przeglądarki już teraz czasami zawieszają i odrzucają 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 zamrażana i odmrażana, nasłuchując zdarzeń freezeresumedocument.

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

Od Chrome 68 obiekt document zawiera teraz właściwość wasDiscarded w Chrome na komputerze (obsługa Androida jest śledzona w tym problemie). Aby sprawdzić, czy strona została odrzucona, gdy była ukryta, możesz sprawdzić wartość tej właściwości w momencie wczytania strony (pamiętaj, że odrzucone strony muszą zostać ponownie załadowane, aby można było ich używać).

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

Aby dowiedzieć się, co jest ważne w przypadku zdarzeń freezeresume, a także jak postępować w przypadku odrzucania stron i jak się do tego przygotować, zapoznaj się z rekomendacjami dla deweloperów dotyczącymi każdego stanu.

W kolejnych sekcjach znajdziesz omówienie tego, jak te nowe funkcje pasują do istniejących stanów i zdarzeń platformy internetowej.

Jak obserwować stany cyklu życia strony w kodzie

W stanach aktywnym, pasywnymukrytym można uruchamiać kod JavaScript, który określa bieżący stan cyklu życia strony na podstawie istniejących interfejsów API platformy internetowej.

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

Stany zamrożonyzakończony można natomiast wykryć tylko w odpowiednich odbiornikach zdarzeń (freezepagehide), ponieważ stan się zmienia.

Jak obserwować zmiany stanu

Korzystając z funkcji getState() zdefiniowanej wcześniej, możesz obserwować wszystkie zmiany stanu PageLifecycle 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 akceptuje następny stan i w przypadku zmiany rejestruje zmiany stanu w konsoli.
  • Dodaje detektory zdarzeń przechwytywania dla wszystkich niezbędnych zdarzeń cyklu życia, które z kolei wywołują funkcję logStateChange(), przekazując następny stan.

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

  • Nie wszystkie zdarzenia związane z cyklem życia strony mają ten sam cel. pagehidepageshow są uruchamiane w przypadku elementu window, visibilitychange, freezeresume są uruchamiane w przypadku elementu document, a focusblur są uruchamiane w przypadku odpowiednich elementów DOM.
  • Większość tych zdarzeń nie jest propagowana, co oznacza, że nie można dodać detektorów zdarzeń bez przechwytywania do wspólnego elementu nadrzędnego i obserwować wszystkich tych zdarzeń.
  • Faza przechwytywania jest wykonywana przed fazami docelową i bąbelkowania, więc dodanie w niej detektorów pomaga zapewnić, że zostaną one uruchomione, zanim inny kod będzie mógł je anulować.

Rekomendacje dla deweloperów dotyczące każdego stanu

Jako programiści musimy rozumieć stany cyklu życia strony i wiedzieć, jak je obserwować w kodzie, ponieważ rodzaj pracy, którą powinniśmy (i nie powinniśmy) wykonywać, zależy w dużej mierze od stanu strony.

Na przykład wyświetlanie użytkownikowi tymczasowego powiadomienia, gdy strona jest ukryta, nie ma sensu. Ten przykład jest dość oczywisty, ale są też inne rekomendacje, które nie są tak oczywiste, a warto je wymienić.

Stan Rekomendacje dla deweloperów
Active

Stan aktywny jest najważniejszy dla użytkownika, a tym samym dla Twojej strony, która powinna wtedy reagować na działania użytkownika.

Wszelkie działania inne niż związane z interfejsem, które mogą blokować wątek główny, powinny mieć niższy priorytet i być wykonywane w  okresach bezczynności lub przenoszone do wątku roboczego.

Passive

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

Gdy strona zmieni stan z aktywnego na pasywny, jest to dobry moment na zapisanie niezapisanych danych aplikacji.

Hidden

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

Przejście do stanu hidden jest też często ostatnią zmianą stanu, którą deweloperzy mogą wiarygodnie zaobserwować (dotyczy to zwłaszcza urządzeń mobilnych, ponieważ użytkownicy mogą zamykać karty lub samą aplikację przeglądarki, a w takich przypadkach zdarzenia beforeunload, pagehideunload nie są wywoływane).

Oznacza to, że stan hidden należy traktować jako prawdopodobny koniec sesji użytkownika. Inaczej mówiąc, zachowaj wszelkie niezapisane stany aplikacji i wyślij wszystkie niewysłane dane analityczne.

Powinieneś też przestać wprowadzać zmiany w interfejsie (ponieważ nie będą one widoczne dla użytkownika) i zatrzymać wszystkie zadania, których użytkownik nie chciałby uruchamiać w tle.

Frozen

W stanie zamrożonym zadania, które można zamrozić kolejkach zadań są zawieszone do momentu odmrożenia 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ą, musisz zatrzymać wszystkie timery i zamknąć wszystkie połączenia, które w stanie 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 wstecz/dalej przeglądarki.

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

Powinieneś też zachować każdy dynamiczny stan widoku (np. pozycję przewijania na liście nieskończonej) w  sessionStorage (lub IndexedDB za pomocą commit()), który chcesz przywrócić, jeśli strona zostanie odrzucona i ponownie wczytana w późniejszym czasie.

Jeśli strona przejdzie ze stanu zamrożonego z powrotem do stanu ukrytego, możesz ponownie otworzyć zamknięte połączenia lub wznowić odpytywanie, które zostało zatrzymane, gdy strona została początkowo zamrożona.

Terminated

Gdy strona przechodzi w stan zakończony, zwykle nie musisz podejmować żadnych działań.

Strony, które są zamykane w wyniku działania użytkownika, zawsze przechodzą w stan ukryty, zanim przejdą w stan zakończony.Dlatego w stanie ukryty należy wykonywać logikę kończącą sesję (np. utrwalanie stanu aplikacji i przesyłanie danych do Analytics).

Jak wspomnieliśmy w rekomendacjach dotyczących stanu ukrytego, deweloperzy muszą pamiętać, że w wielu przypadkach (zwłaszcza na urządzeniach mobilnych) nie można niezawodnie wykryć przejścia do stanu zakończonego.Dlatego deweloperzy, którzy korzystają ze zdarzeń zakończenia (np. beforeunload, pagehideunload), prawdopodobnie tracą dane.

Discarded

Stan odrzucony nie jest widoczny dla deweloperów w momencie odrzucenia strony. Dzieje się tak, ponieważ strony są zwykle zamykane z powodu ograniczeń zasobów, a odmrażanie strony tylko po to, aby umożliwić uruchomienie skryptu w odpowiedzi na zdarzenie zamknięcia, jest w większości przypadków niemożliwe.

W związku z tym musisz się przygotować na możliwość odrzucenia zmiany ze stanu ukryty na zamrożony. Następnie możesz zareagować na przywrócenie odrzuconej strony w momencie jej wczytywania, sprawdzając document.wasDiscarded.

Ponieważ niezawodność i kolejność zdarzeń cyklu życia nie są spójnie wdrażane we wszystkich przeglądarkach, najłatwiejszym sposobem na zastosowanie się do zaleceń z tabeli jest użycie pliku PageLifecycle.js.

Starsze interfejsy API cyklu życia, których należy unikać

W miarę możliwości należy unikać tych zdarzeń.

Zdarzenie unload

Wielu programistów traktuje zdarzenie unload jako gwarantowane wywołanie zwrotne i używa go jako sygnału zakończenia sesji do zapisywania stanu i wysyłania danych analitycznych, ale takie postępowanie jest wyjątkowo zawodne, zwłaszcza na urządzeniach mobilnych. Zdarzenie unload nie jest wywoływane w wielu typowych sytuacjach zamykania, w tym podczas zamykania karty w przełączniku kart na urządzeniu mobilnym lub zamykania aplikacji przeglądarki w przełączniku aplikacji.

Z tego powodu zawsze lepiej jest polegać na zdarzeniu visibilitychange, aby określić, kiedy kończy się sesja, a stan ukryty traktować jako ostatni wiarygodny moment na zapisanie danych aplikacji i użytkownika.

Ponadto sama obecność zarejestrowanego modułu obsługi zdarzeń unload (za pomocą onunload lub addEventListener()) może uniemożliwić przeglądarkom umieszczanie stron w pamięci podręcznej stanu strony internetowej, co przyspiesza wczytywanie stron po kliknięciu przycisków Wstecz i Dalej.

We wszystkich nowoczesnych przeglądarkach zalecamy, aby do wykrywania możliwych zamknięć strony (czyli stanu zakończono) używać zdarzenia pagehide, a nie zdarzenia unload. Jeśli musisz obsługiwać Internet Explorer w wersjach 10 i starszych, wykrywaj zdarzenie pagehide i używaj 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żliwiać kwalifikowanie się stron do pamięci podręcznej stanu strony internetowej. Nowoczesne przeglądarki nie mają tego ograniczenia. Niektóre przeglądarki w ramach środków ostrożności nie wywołują zdarzenia beforeunload podczas próby umieszczenia strony w pamięci podręcznej wstecz/dalej, 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 na stronie, zanim zezwolą na wywołanie zdarzenia beforeunload, co dodatkowo wpływa na jego niezawodność.

Jedną z różnic między beforeunloadunload jest to, że beforeunload może być używane w legalny sposób. Na przykład gdy chcesz ostrzec użytkownika, że ma niezapisane zmiany, które zostaną utracone, jeśli będzie kontynuować zamykanie strony.

Istnieją uzasadnione powody, dla których warto używać beforeunload, dlatego zalecamy dodawanie odbiorników beforeunload tylko wtedy, gdy użytkownik ma niezapisane zmiany, a następnie natychmiastowe usuwanie ich po zapisaniu zmian.

Innymi słowy, nie rób tego (ponieważ dodaje to słuchacza beforeunload bezwarunkowo):

addEventListener('beforeunload', (event) => {   // A function that returns `true` if the page has unsaved changes.   if (pageHasUnsavedChanges()) {     event.preventDefault();      // Legacy support for older browsers.     event.returnValue = true;   } }); 

Zamiast tego zrób tak (ponieważ dodaje on odbiornik beforeunload tylko wtedy, gdy jest potrzebny, i usuwa go, gdy nie jest):

const beforeUnloadListener = (event) => {   event.preventDefault();    // Legacy support for older browsers.   event.returnValue = true; };  // A function that adds a `beforeunload` listener if there are unsaved changes. onPageHasUnsavedChanges(() => {   addEventListener('beforeunload', beforeUnloadListener); });  // A function that removes the `beforeunload` listener when the page's unsaved // changes are resolved. onAllChangesSaved(() => {   removeEventListener('beforeunload', beforeUnloadListener); }); 

Najczęstsze pytania

Dlaczego nie ma stanu „wczytywanie”?

Interfejs Page Lifecycle API definiuje stany jako odrębne i wykluczające się nawzajem. Strona może być wczytywana w stanie aktywnym, pasywnym lub ukrytym, a przed zakończeniem wczytywania może zmienić stan lub nawet zostać zamknięta. Dlatego w tym paradygmacie nie ma sensu oddzielny stan wczytywania.

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, gdy działają w stanie ukrytym. Najbardziej oczywistym przykładem jest aplikacja do odtwarzania muzyki.

Są też sytuacje, w których odrzucenie strony przez Chrome byłoby ryzykowne, np. gdy zawiera ona formularz z nieprzesłanymi danymi wprowadzonymi przez użytkownika lub gdy ma beforeunload procedurę obsługi, która ostrzega, gdy strona jest zamykana.

Na razie Chrome będzie ostrożnie odrzucać strony i będzie to robić 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ą dowolną z tych czynności, nie zostaną odrzucone, chyba że wystąpią ekstremalne ograniczenia zasobów:

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

Aktualną listę funkcji używanych do określania, czy kartę można bezpiecznie zamrozić lub odrzucić, znajdziesz w artykule Heurystyka zamrażania i odrzucania 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ą wdrażają niektóre przeglądarki, 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 użyje przycisków Wstecz lub Dalej. Pamiętaj, że dodanie unload funkcji obsługi zdarzeń uniemożliwia przeprowadzenie tej optymalizacji.

Z praktycznego punktu widzenia to zamrożenie jest funkcjonalnie takie samo jak zamrożenie, które przeglądarki wykonują w celu oszczędzania procesora i baterii. Z tego powodu jest ono uważane za część stanu cyklu życia zamrożonego.

Jeśli nie mogę uruchamiać asynchronicznych interfejsów API w stanie zamrożenia lub zakończenia, jak mogę zapisywać dane w IndexedDB?

W stanach zamrożenia i zakończenia zadania, które można zamrozićkolejkach zadań na stronie są zawieszane, co oznacza, że nie można niezawodnie korzystać z asynchronicznych interfejsów API opartych na wywołaniach zwrotnych.

Większość interfejsów API IndexedDB jest oparta na wywołaniach zwrotnych, ale metoda commit() w interfejsie IDBTransaction umożliwia rozpoczęcie procesu zatwierdzania aktywnej transakcji bez czekania na wysłanie zdarzeń z oczekujących żądań. Zapewnia to niezawodny sposób zapisywania danych w bazie danych IndexedDB w freeze lub visibilitychange, ponieważ zatwierdzenie jest wykonywane od razu, a nie umieszczane w kolejce w osobnym zadaniu.

Testowanie aplikacji w stanie zamrożenia i odrzucenia

Aby sprawdzić, jak aplikacja zachowuje się w stanie zamrożenia i odrzucenia, możesz przejść na stronę chrome://discards, aby zamrozić lub odrzucić dowolną z otwartych kart.

Interfejs odrzucania Chrome
Interfejs odrzucania kart w Chrome

Dzięki temu możesz mieć pewność, że strona prawidłowo obsługuje zdarzenia freezeresume, a także flagę document.wasDiscarded, gdy strony są ponownie wczytywane po odrzuceniu.

Podsumowanie

Programiści, którzy chcą oszczędzać 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, w których użytkownik nie spodziewa się takiego zużycia.

Im więcej deweloperów zacznie wdrażać nowe interfejsy Page Lifecycle API, tym bezpieczniejsze będzie zamrażanie i odrzucanie przez przeglądarki nieużywanych stron. Oznacza to, że przeglądarki będą zużywać mniej pamięci, mocy procesora, baterii i zasobów sieciowych, co jest korzystne dla użytkowników.