Paginalevenscyclus-API

Browserondersteuning

  • Chroom: 68.
  • Rand: 79.
  • Firefox: niet ondersteund.
  • Safari: niet ondersteund.

Moderne browsers zullen tegenwoordig soms pagina's onderbreken of volledig verwijderen als de systeembronnen beperkt zijn. In de toekomst willen browsers dit proactief doen, zodat ze minder stroom en geheugen verbruiken. De Page Lifecycle API biedt lifecycle hooks zodat uw pagina's deze browserinterventies veilig kunnen verwerken zonder de gebruikerservaring te beïnvloeden. Bekijk de API om te zien of u deze functies in uw applicatie zou moeten implementeren.

Achtergrond

De levenscyclus van applicaties is een belangrijke manier waarop moderne besturingssystemen bronnen beheren. Op Android-, iOS- en recente Windows-versies kunnen apps op elk moment door het besturingssysteem worden gestart en gestopt. Hierdoor kunnen deze platforms middelen stroomlijnen en opnieuw toewijzen waar de gebruiker het beste van profiteert.

Op internet bestaat er historisch gezien geen dergelijke levenscyclus, en apps kunnen voor onbepaalde tijd in leven worden gehouden. Als er grote aantallen webpagina's actief zijn, kunnen kritieke systeembronnen zoals geheugen, CPU, batterij en netwerk overbelast raken, wat leidt tot een slechte eindgebruikerservaring.

Hoewel het webplatform al lang gebeurtenissen kent die verband houden met levenscyclusstatussen (zoals load , unload en visibilitychange ), kunnen ontwikkelaars met deze gebeurtenissen alleen reageren op door de gebruiker geïnitieerde veranderingen in de levenscyclusstatus. Om het web betrouwbaar te laten werken op apparaten met een laag vermogen (en in het algemeen meer hulpbronnenbewust te zijn op alle platforms) hebben browsers een manier nodig om proactief systeembronnen terug te winnen en opnieuw toe te wijzen.

In feite nemen browsers tegenwoordig al actieve maatregelen om bronnen te besparen voor pagina's op achtergrondtabbladen, en veel browsers (vooral Chrome) zouden dit nog veel meer willen doen: om hun totale hulpbronnenvoetafdruk te verkleinen.

Het probleem is dat ontwikkelaars zich niet kunnen voorbereiden op dit soort door het systeem geïnitieerde interventies, of zelfs maar weten dat ze plaatsvinden. Dit betekent dat browsers conservatief moeten zijn, anders lopen ze het risico webpagina's kapot te maken.

De Page Lifecycle API probeert dit probleem op te lossen door:

  • Het introduceren en standaardiseren van het concept van levenscyclusstatussen op internet.
  • Het definiëren van nieuwe, door het systeem geïnitieerde statussen waarmee browsers de bronnen kunnen beperken die kunnen worden verbruikt door verborgen of inactieve tabbladen.
  • Het creëren van nieuwe API's en gebeurtenissen waarmee webontwikkelaars kunnen reageren op overgangen van en naar deze nieuwe, door het systeem geïnitieerde toestanden.

Deze oplossing biedt de voorspelbaarheid die webontwikkelaars nodig hebben om applicaties te bouwen die bestand zijn tegen systeeminterventies, en stelt browsers in staat de systeembronnen agressiever te optimaliseren, wat uiteindelijk ten goede komt aan alle internetgebruikers.

In de rest van dit bericht worden de nieuwe functies voor de paginalevenscyclus geïntroduceerd en wordt onderzocht hoe deze zich verhouden tot alle bestaande webplatformstatussen en -gebeurtenissen. Het zal ook aanbevelingen en best practices geven voor het soort werk dat ontwikkelaars in elke staat zouden moeten (en niet zouden moeten) doen.

Overzicht van statussen en gebeurtenissen van de paginalevenscyclus

Alle statussen van de paginalevenscyclus zijn afzonderlijk en sluiten elkaar uit, wat betekent dat een pagina zich slechts in één status tegelijk kan bevinden. En de meeste wijzigingen in de levenscyclusstatus van een pagina zijn over het algemeen waarneembaar via DOM-gebeurtenissen (zie de aanbevelingen van ontwikkelaars voor elke status voor de uitzonderingen).

Misschien wel de gemakkelijkste manier om de statussen van de paginalevenscyclus uit te leggen – evenals de gebeurtenissen die overgangen daartussen signaleren – is met een diagram:

Een visuele weergave van de status- en gebeurtenisstroom die in dit document wordt beschreven.
Paginalevenscyclus API-status en gebeurtenisstroom.

Staten

In de volgende tabel wordt elke status gedetailleerd uitgelegd. Het vermeldt ook de mogelijke toestanden die ervoor en erna kunnen optreden, evenals de gebeurtenissen die ontwikkelaars kunnen gebruiken om veranderingen waar te nemen.

Staat Beschrijving
Actief

Een pagina is actief als deze zichtbaar is en invoerfocus heeft.

Mogelijke eerdere toestanden:
passief (via de focus )
bevroren (via de resume gebeurtenis en vervolgens de pageshow gebeurtenis)

Mogelijke volgende toestanden:
passief (via de blur )

Passief

Een pagina bevindt zich in de passieve status als deze zichtbaar is en geen invoerfocus heeft.

Mogelijke eerdere toestanden:
actief (via de blur )
verborgen (via de visibilitychange )
bevroren (via de resume gebeurtenis en vervolgens de pageshow gebeurtenis)

Mogelijke volgende toestanden:
actief (via de focus )
verborgen (via de visibilitychange )

Verborgen

Een pagina bevindt zich in de verborgen status als deze niet zichtbaar is (en niet is bevroren, weggegooid of beëindigd).

Mogelijke eerdere toestanden:
passief (via het visibilitychange )
bevroren (via de resume gebeurtenis en vervolgens de pageshow gebeurtenis)

Mogelijke volgende toestanden:
passief (via het visibilitychange )
bevroren (via de freeze )
weggegooid (geen evenementen afgevuurd)
beëindigd (geen evenementen afgevuurd)

Bevroren

In de bevroren toestand schort de browser de uitvoering van bevriezingstaken in de taakwachtrijen van de pagina op totdat de pagina wordt gedeblokkeerd. Dit betekent dat zaken als JavaScript-timers en ophaal-callbacks niet worden uitgevoerd. Reeds uitgevoerde taken kunnen worden voltooid (het allerbelangrijkste: de terugroepactie freeze ), maar deze kunnen beperkt zijn in wat ze kunnen doen en hoe lang ze kunnen worden uitgevoerd.

Browsers bevriezen pagina's als een manier om het CPU-/batterij-/datagebruik te behouden; ze doen het ook als een manier om snellere terug-/vooruitnavigatie mogelijk te maken, waardoor de noodzaak voor het opnieuw laden van een volledige pagina wordt vermeden.

Mogelijke eerdere toestanden:
verborgen (via de freeze )

Mogelijke volgende toestanden:
actief (via de resume gebeurtenis en vervolgens de pageshow gebeurtenis)
passief (via de resume gebeurtenis en vervolgens de pageshow gebeurtenis)
verborgen (via de resume gebeurtenis)
weggegooid (geen evenementen afgevuurd)

Beëindigd

Een pagina bevindt zich in de beëindigde status zodra deze door de browser wordt verwijderd en uit het geheugen wordt gewist. In deze status kunnen geen nieuwe taken worden gestart en lopende taken kunnen worden beëindigd als ze te lang duren.

Mogelijke eerdere toestanden:
verborgen (via de pagehide -gebeurtenis)

Mogelijke volgende toestanden:
GEEN

Weggegooid

Een pagina bevindt zich in de weggegooide status wanneer deze door de browser wordt verwijderd om bronnen te besparen. In deze staat kunnen geen taken, gebeurtenis-callbacks of JavaScript van welke aard dan ook worden uitgevoerd, omdat het weggooien doorgaans plaatsvindt onder beperkte middelen, waardoor het starten van nieuwe processen onmogelijk is.

In de verwijderde status is het tabblad zelf (inclusief de tabbladtitel en favicon) meestal zichtbaar voor de gebruiker, ook al is de pagina verdwenen.

Mogelijke eerdere toestanden:
verborgen (geen evenementen geactiveerd)
bevroren (geen evenementen afgevuurd)

Mogelijke volgende toestanden:
GEEN

Evenementen

Browsers verzenden veel gebeurtenissen, maar slechts een klein deel ervan signaleert een mogelijke verandering in de levenscyclusstatus van de pagina. De volgende tabel geeft een overzicht van alle gebeurtenissen die betrekking hebben op de levenscyclus en geeft aan van welke status ze kunnen overgaan.

Naam Details
focus

Een DOM-element heeft focus gekregen.

Opmerking: een focus duidt niet noodzakelijkerwijs op een toestandsverandering. Het signaleert alleen een statusverandering als de pagina voorheen geen invoerfocus had.

Mogelijke eerdere toestanden:
passief

Mogelijke huidige toestanden:
actief

blur

Een DOM-element heeft de focus verloren.

Opmerking: een blur duidt niet noodzakelijkerwijs op een statusverandering. Het signaleert alleen een statusverandering als de pagina niet langer de invoerfocus heeft (dat wil zeggen dat de pagina niet zomaar de focus van het ene element naar het andere heeft gewijzigd).

Mogelijke eerdere toestanden:
actief

Mogelijke huidige toestanden:
passief

visibilitychange

De visibilityState van het document is gewijzigd. Dit kan gebeuren wanneer een gebruiker naar een nieuwe pagina navigeert, van tabblad wisselt, een tabblad sluit, de browser minimaliseert of sluit, of van app wisselt op mobiele besturingssystemen.

Mogelijke eerdere toestanden:
passief
verborgen

Mogelijke huidige toestanden:
passief
verborgen

freeze *

De pagina is zojuist bevroren. Elke bevriesbare taak in de taakwachtrijen van de pagina wordt niet gestart.

Mogelijke eerdere toestanden:
verborgen

Mogelijke huidige toestanden:
bevroren

resume *

De browser heeft een bevroren pagina hervat.

Mogelijke eerdere toestanden:
bevroren

Mogelijke huidige toestanden:
actief (indien gevolgd door de pageshow gebeurtenis)
passief (indien gevolgd door de pageshow gebeurtenis)
verborgen

pageshow

Er wordt naar een sessiegeschiedenisitem gepasseerd.

Dit kan een geheel nieuwe pagina zijn die is geladen of een pagina die uit de back/forward cache is gehaald. Als de pagina uit de back/forward cache is gehaald, is de persisted eigenschap van de gebeurtenis true , anders is deze false .

Mogelijke eerdere toestanden:
bevroren (een resume zou ook zijn geactiveerd)

Mogelijke huidige toestanden:
actief
passief
verborgen

pagehide

Er wordt vanuit een sessiegeschiedenisitem gepasseerd.

Als de gebruiker naar een andere pagina navigeert en de browser de huidige pagina aan de back/forward-cache kan toevoegen om deze later opnieuw te gebruiken, is de persisted eigenschap van de gebeurtenis true . Wanneer true , komt de pagina in de bevroren toestand terecht, anders in de beëindigde toestand.

Mogelijke eerdere toestanden:
verborgen

Mogelijke huidige toestanden:
bevroren ( event.persisted is waar, freeze volgt)
beëindigd ( event.persisted is false, unload -gebeurtenis volgt)

beforeunload

Het venster, het document en de bijbehorende bronnen staan ​​op het punt te worden verwijderd. Het document is nog steeds zichtbaar en de gebeurtenis kan op dit moment nog steeds worden geannuleerd.

Belangrijk: de gebeurtenis beforeunload mag alleen worden gebruikt om de gebruiker te waarschuwen voor niet-opgeslagen wijzigingen. Zodra deze wijzigingen zijn opgeslagen, moet de gebeurtenis worden verwijderd. Het mag nooit onvoorwaardelijk aan de pagina worden toegevoegd, omdat dit in sommige gevallen de prestaties kan schaden. Zie de sectie verouderde API's voor meer informatie.

Mogelijke eerdere toestanden:
verborgen

Mogelijke huidige toestanden:
beëindigd

unload

De pagina wordt verwijderd.

Waarschuwing: het gebruik van de unload -gebeurtenis wordt nooit aanbevolen, omdat dit onbetrouwbaar is en in sommige gevallen de prestaties kan schaden. Zie de sectie verouderde API's voor meer details.

Mogelijke eerdere toestanden:
verborgen

Mogelijke huidige toestanden:
beëindigd

* Geeft een nieuwe gebeurtenis aan die is gedefinieerd door de Page Lifecycle API

Nieuwe functies toegevoegd in Chrome 68

Het vorige diagram toont twee toestanden die door het systeem worden geïnitieerd in plaats van door de gebruiker: bevroren en weggegooid . Zoals eerder vermeld, bevriezen browsers tegenwoordig al af en toe en verwijderen ze verborgen tabbladen (naar eigen goeddunken), maar ontwikkelaars kunnen niet weten wanneer dit gebeurt.

In Chrome 68 kunnen ontwikkelaars nu waarnemen wanneer een verborgen tabblad wordt bevroren en gedeblokkeerd door te luisteren naar de freeze en resume gebeurtenissen op document .

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

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

Vanaf Chrome 68 bevat het document nu een wasDiscarded eigenschap in desktop-Chrome ( Android-ondersteuning wordt in dit nummer bijgehouden ). Om te bepalen of een pagina is verwijderd terwijl deze zich op een verborgen tabblad bevond, kunt u de waarde van deze eigenschap controleren tijdens het laden van de pagina (opmerking: verwijderde pagina's moeten opnieuw worden geladen om ze opnieuw te kunnen gebruiken).

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

Voor advies over wat belangrijk is om te doen bij het freeze en resume gebeurtenissen, en hoe u moet omgaan met en voorbereiden op het weggooien van pagina's, raadpleegt u de aanbevelingen voor ontwikkelaars voor elke status .

De volgende secties bieden een overzicht van hoe deze nieuwe functies passen in de bestaande statussen en gebeurtenissen van het webplatform.

Hoe u de status van de paginalevenscyclus in code kunt observeren

In de actieve , passieve en verborgen status is het mogelijk JavaScript-code uit te voeren die de huidige paginalevenscyclusstatus bepaalt op basis van bestaande webplatform-API's.

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

De bevroren en beëindigde toestanden kunnen daarentegen alleen worden gedetecteerd in hun respectievelijke gebeurtenislistener ( freeze en pagehide ) wanneer de toestand verandert.

Hoe toestandsveranderingen te observeren

Voortbouwend op de eerder gedefinieerde functie getState() kunt u alle wijzigingen in de status van de paginalevenscyclus waarnemen met de volgende code.

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

Deze code doet drie dingen:

  • Stelt de initiële status in met behulp van de getState() functie.
  • Definieert een functie die een volgende status accepteert en, als er een wijziging is, de statuswijzigingen in de console registreert.
  • Voegt vastleggen van gebeurtenislisteners toe voor alle noodzakelijke levenscyclusgebeurtenissen, die op hun beurt logStateChange() aanroepen en de volgende status doorgeven.

Een ding om op te merken over de code is dat alle gebeurtenislisteners aan window worden toegevoegd en dat ze allemaal {capture: true} doorgeven. Hier zijn een paar redenen voor:

  • Niet alle paginalevenscyclusgebeurtenissen hebben hetzelfde doel. pagehide en pageshow worden op window geactiveerd; visibilitychange , freeze en resume worden op document afgevuurd, en focus en blur worden op hun respectieve DOM-elementen afgevuurd.
  • De meeste van deze gebeurtenissen borrelen niet, wat betekent dat het onmogelijk is om niet-vastleggende gebeurtenislisteners toe te voegen aan een gemeenschappelijk voorouderelement en ze allemaal te observeren.
  • De capture-fase wordt uitgevoerd vóór de target- of bubble-fase, dus door daar luisteraars aan toe te voegen, zorg je ervoor dat ze worden uitgevoerd voordat andere code ze kan annuleren.

Aanbevelingen voor ontwikkelaars voor elke staat

Als ontwikkelaars is het belangrijk om zowel de status van de paginalevenscyclus te begrijpen als te weten hoe u deze in code kunt observeren, omdat het soort werk dat u wel (en niet zou moeten) doen, grotendeels afhangt van de staat waarin uw pagina zich bevindt.

Het heeft bijvoorbeeld duidelijk geen zin om een ​​tijdelijke melding aan de gebruiker weer te geven als de pagina zich in de verborgen status bevindt. Hoewel dit voorbeeld vrij duidelijk is, zijn er nog andere aanbevelingen die niet zo voor de hand liggend zijn en die de moeite waard zijn om op te sommen.

Staat Aanbevelingen van ontwikkelaars
Active

De actieve status is het meest kritieke moment voor de gebruiker en dus het belangrijkste moment waarop uw pagina reageert op gebruikersinvoer .

Alle niet-UI-werkzaamheden die de hoofdthread kunnen blokkeren, moeten worden gedeprioriteerd naar inactieve perioden of worden overgedragen aan een webwerker .

Passive

In de passieve toestand heeft de gebruiker geen interactie met de pagina, maar kan hij deze nog steeds zien. Dit betekent dat UI-updates en animaties nog steeds soepel moeten verlopen, maar dat de timing van deze updates minder kritisch is.

Wanneer de pagina verandert van active naar passief , is het een goed moment om de niet-opgeslagen applicatiestatus te behouden.

Hidden

Wanneer de pagina verandert van passief naar verborgen , is het mogelijk dat de gebruiker er pas weer mee communiceert nadat deze opnieuw is geladen.

De overgang naar verborgen is ook vaak de laatste statusverandering die betrouwbaar waarneembaar is door ontwikkelaars (dit geldt vooral op mobiele apparaten, omdat gebruikers tabbladen of de browser-app zelf kunnen sluiten, en de beforeunload , pagehide en unload gebeurtenissen worden in die gevallen niet geactiveerd ).

Dit betekent dat u de verborgen status moet beschouwen als het waarschijnlijke einde van de sessie van de gebruiker. Met andere woorden: bewaar de niet-opgeslagen applicatiestatus en stuur eventuele niet-verzonden analysegegevens.

U moet ook stoppen met het maken van UI-updates (aangezien deze niet door de gebruiker worden gezien) en u moet alle taken stoppen die een gebruiker niet op de achtergrond wil uitvoeren.

Frozen

In de bevroren toestand worden bevriezingstaken in de taakwachtrijen opgeschort totdat de pagina wordt gedeblokkeerd - wat misschien nooit zal gebeuren (bijvoorbeeld als de pagina wordt weggegooid).

Dit betekent dat wanneer de pagina verandert van verborgen naar bevroren, het van essentieel belang is dat u timers stopt of verbindingen verbreekt die, indien bevroren, van invloed kunnen zijn op andere geopende tabbladen in dezelfde oorsprong, of op het vermogen van de browser om de pagina op de achtergrond te plaatsen. voorwaartse cache .

Het is vooral belangrijk dat u:

Je moet ook elke dynamische weergavestatus (bijvoorbeeld schuifpositie in een oneindige lijstweergave) behouden in sessionStorage (of IndexedDB via commit() ) die je wilt herstellen als de pagina wordt verwijderd en later opnieuw wordt geladen.

Als de pagina van bevroren terug naar verborgen gaat, kunt u alle gesloten verbindingen opnieuw openen of de polling opnieuw starten die u hebt gestopt toen de pagina aanvankelijk bevroren was.

Terminated

Normaal gesproken hoeft u geen actie te ondernemen wanneer een pagina overgaat naar de beëindigde status.

Omdat pagina's die worden verwijderd als resultaat van een gebruikersactie, altijd de verborgen status doorlopen voordat ze in de beëindigde status terechtkomen, is de verborgen status de plek waar logica voor het beëindigen van de sessie (bijvoorbeeld het aanhouden van de applicatiestatus en rapportage aan analyses) moet worden uitgevoerd.

Ook (zoals vermeld in de aanbevelingen voor de verborgen status ) is het erg belangrijk voor ontwikkelaars om te beseffen dat de overgang naar de beëindigde status in veel gevallen niet op betrouwbare wijze kan worden gedetecteerd (vooral op mobiel), dus ontwikkelaars die afhankelijk zijn van beëindigingsgebeurtenissen (bijv. beforeunload , pagehide en unload ) verliezen waarschijnlijk gegevens.

Discarded

De weggegooide status is niet waarneembaar voor ontwikkelaars op het moment dat een pagina wordt weggegooid. Dit komt omdat pagina's doorgaans worden weggegooid onder beperkte middelen, en het deblokkeren van een pagina alleen maar om het script uit te voeren als reactie op een wegwerpgebeurtenis in de meeste gevallen eenvoudigweg niet mogelijk is.

Als gevolg hiervan moet u zich voorbereiden op de mogelijkheid van weggooien bij de wijziging van verborgen naar bevroren , en vervolgens kunt u reageren op het herstel van een weggegooide pagina tijdens het laden van de pagina door document.wasDiscarded aan te vinken.

Nogmaals, aangezien de betrouwbaarheid en volgorde van levenscyclusgebeurtenissen niet consistent in alle browsers zijn geïmplementeerd, is de eenvoudigste manier om het advies in de tabel op te volgen het gebruik van PageLifecycle.js .

Verouderde levenscyclus-API's moeten worden vermeden

De volgende gebeurtenissen moeten waar mogelijk worden vermeden.

Het ontlaadevenement

Veel ontwikkelaars beschouwen de unload -gebeurtenis als een gegarandeerde callback en gebruiken deze als een signaal aan het einde van de sessie om de status op te slaan en analysegegevens te verzenden, maar dit doen is uiterst onbetrouwbaar , vooral op mobiel! De unload -gebeurtenis wordt in veel typische unload-situaties niet geactiveerd, inclusief het sluiten van een tabblad via de tabbladwisselaar op mobiel of het sluiten van de browserapp via de appwisselaar.

Om deze reden is het altijd beter om te vertrouwen op de visibilitychange om te bepalen wanneer een sessie eindigt, en de verborgen status te beschouwen als de laatste betrouwbare tijd om app- en gebruikersgegevens op te slaan .

Bovendien kan alleen al de aanwezigheid van een geregistreerde unload -gebeurtenishandler (via onunload of addEventListener() ) voorkomen dat browsers pagina's in de back/forward-cache kunnen plaatsen voor sneller terug- en vooruitladen.

In alle moderne browsers wordt aanbevolen om altijd de pagehide -gebeurtenis te gebruiken om mogelijke verwijderingen van pagina's te detecteren (ook wel de beëindigde status genoemd) in plaats van de unload -gebeurtenis. Als u Internet Explorer versie 10 en lager moet ondersteunen, moet u de pagehide gebeurtenis detecteren en alleen unload gebruiken als de browser pagehide niet ondersteunt:

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

De beforeunload-gebeurtenis

De gebeurtenis beforeunload heeft een soortgelijk probleem als de gebeurtenis unload , in die zin dat historisch gezien de aanwezigheid van een gebeurtenis beforeunload ervoor kon zorgen dat pagina's niet in aanmerking komen voor back/forward cache . Moderne browsers hebben deze beperking niet. Hoewel sommige browsers uit voorzorg de gebeurtenis beforeunload niet activeren wanneer ze proberen een pagina in de back/forward cache te plaatsen, wat betekent dat de gebeurtenis niet betrouwbaar is als signaal voor het einde van de sessie. Bovendien vereisen sommige browsers (waaronder Chrome ) een gebruikersinteractie op de pagina voordat de beforeunload gebeurtenis kan worden geactiveerd, wat de betrouwbaarheid ervan verder beïnvloedt.

Een verschil tussen beforeunload en unload is dat er legitiem gebruik is van beforeunload . Als u de gebruiker bijvoorbeeld wilt waarschuwen dat er niet-opgeslagen wijzigingen zijn, gaan deze verloren als hij doorgaat met het verwijderen van de pagina.

Omdat er geldige redenen zijn om beforeunload te gebruiken, is het raadzaam beforeunload listeners alleen toe te voegen wanneer een gebruiker niet-opgeslagen wijzigingen heeft en deze vervolgens onmiddellijk te verwijderen nadat ze zijn opgeslagen.

Met andere woorden, doe dit niet (aangezien het onvoorwaardelijk een beforeunload listener toevoegt):

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

Doe dit in plaats daarvan (aangezien het alleen de beforeunload -listener toevoegt wanneer dit nodig is, en verwijdert wanneer dit niet het geval is):

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

Veelgestelde vragen

Waarom is er geen status 'laden'?

De Page Lifecycle API definieert statussen als discreet en wederzijds uitsluitend. Omdat een pagina kan worden geladen in de actieve, passieve of verborgen status, en omdat deze van status kan veranderen (of zelfs kan worden beëindigd) voordat het laden is voltooid, is een afzonderlijke laadstatus binnen dit paradigma niet zinvol.

Mijn pagina doet belangrijk werk als deze verborgen is. Hoe kan ik voorkomen dat deze wordt bevroren of verwijderd?

Er zijn veel legitieme redenen waarom webpagina's niet mogen worden bevroren terwijl ze in de verborgen staat worden uitgevoerd. Het meest voor de hand liggende voorbeeld is een app die muziek afspeelt.

Er zijn ook situaties waarin het voor Chrome riskant zou zijn om een ​​pagina te verwijderen, bijvoorbeeld als deze een formulier bevat met niet-ingediende gebruikersinvoer, of als er een beforeunload -handler is die waarschuwt wanneer de pagina wordt verwijderd.

Op dit moment zal Chrome conservatief zijn bij het weggooien van pagina's en dit alleen doen als het er zeker van is dat dit geen gevolgen heeft voor gebruikers. Pagina's waarvan is waargenomen dat ze een van de volgende handelingen uitvoeren terwijl ze zich in de verborgen status bevinden, worden bijvoorbeeld niet verwijderd, tenzij onder extreme beperkingen van de middelen:

  • Audio afspelen
  • WebRTC gebruiken
  • De tabeltitel of favicon bijwerken
  • Waarschuwingen weergeven
  • Pushmeldingen verzenden

Voor de huidige lijstfuncties die worden gebruikt om te bepalen of een tabblad veilig kan worden bevroren of verwijderd, zie: Heuristieken voor bevriezen en verwijderen in Chrome.

Wat is de back/forward-cache?

De back/forward-cache is een term die wordt gebruikt om een ​​navigatie-optimalisatie te beschrijven die sommige browsers implementeren en die het gebruik van de back- en forward-knoppen sneller maakt.

Wanneer een gebruiker een pagina verlaat, bevriezen deze browsers een versie van die pagina, zodat deze snel kan worden hervat als de gebruiker terug navigeert met de knoppen Vorige of Volgende. Houd er rekening mee dat het toevoegen van een unload -gebeurtenishandler verhindert dat deze optimalisatie mogelijk is .

In alle opzichten is dit bevriezen functioneel hetzelfde als wat de bevriezingsbrowsers doen om de CPU/batterij te sparen; om die reden wordt het beschouwd als onderdeel van de bevroren levenscyclusstatus.

Als ik geen asynchrone API's in de bevroren of beëindigde status kan uitvoeren, hoe kan ik dan gegevens opslaan in IndexedDB?

In bevroren en beëindigde toestanden worden bevriesbare taken in de taakwachtrijen van een pagina opgeschort, wat betekent dat asynchrone en op callback gebaseerde API's zoals IndexedDB niet betrouwbaar kunnen worden gebruikt.

In de toekomst zullen we een methode commit() toevoegen aan IDBTransaction objecten, waardoor ontwikkelaars een manier krijgen om in feite alleen-schrijven-transacties uit te voeren waarvoor geen callbacks nodig zijn. Met andere woorden: als de ontwikkelaar alleen maar gegevens naar IndexedDB schrijft en geen complexe transactie uitvoert die bestaat uit lees- en schrijfbewerkingen, kan de methode commit() worden voltooid voordat taakwachtrijen worden opgeschort (ervan uitgaande dat de IndexedDB-database al open is).

Voor code die vandaag de dag moet werken, hebben ontwikkelaars echter twee opties:

  • Gebruik sessieopslag: Sessieopslag is synchroon en blijft behouden wanneer pagina's worden weggegooid.
  • Gebruik IndexedDB van uw servicemedewerker: een servicemedewerker kan gegevens opslaan in IndexedDB nadat de pagina is beëindigd of verwijderd. In de gebeurtenislistener freeze of pagehide kunt u gegevens naar uw servicemedewerker sturen via postMessage() , waarna de servicemedewerker de gegevens kan opslaan.

Uw app testen in de bevroren en afgedankte status

Om te testen hoe uw app zich gedraagt ​​in de bevroren en weggegooide status, kunt u naar chrome://discards gaan om uw geopende tabbladen daadwerkelijk te bevriezen of te verwijderen.

Chrome negeert de gebruikersinterface
Chrome negeert de gebruikersinterface

Hierdoor kunt u ervoor zorgen dat uw pagina de freeze en resume correct afhandelt, evenals de vlag document.wasDiscarded wanneer pagina's opnieuw worden geladen na het weggooien.

Samenvatting

Ontwikkelaars die de systeembronnen van de apparaten van hun gebruikers willen respecteren, moeten hun apps bouwen met de status van de paginalevenscyclus in gedachten. Het is van cruciaal belang dat webpagina's niet buitensporig veel systeembronnen verbruiken in situaties die de gebruiker niet zou verwachten

Hoe meer ontwikkelaars de nieuwe Page Lifecycle API's gaan implementeren, hoe veiliger het voor browsers zal zijn om pagina's die niet worden gebruikt, te bevriezen en te verwijderen. Dit betekent dat browsers minder geheugen, CPU, batterij en netwerkbronnen verbruiken, wat een overwinning is voor gebruikers.