Supporto dei browser
I browser moderni a volte sospendono le pagine o le ignorano del tutto quando le risorse di sistema sono limitate. In futuro, i browser vorranno eseguire questa operazione in modo proattivo, in modo da consumare meno energia e memoria. L'API Page Lifecycle fornisce hook di ciclo di vita in modo che le tue pagine possano gestire in sicurezza questi interventi del browser senza influire sull'esperienza utente. Dai un'occhiata all'API per vedere se devi implementare queste funzionalità nella tua applicazione.
Sfondo
Il ciclo di vita delle applicazioni è un modo fondamentale in cui i sistemi operativi moderni gestiscono le risorse. Su Android, iOS e versioni recenti di Windows, le app possono essere avviate e interrotte in qualsiasi momento dal sistema operativo. In questo modo, queste piattaforme possono semplificare e riallocare le risorse dove sono più utili per l'utente.
Sul web, storicamente, non è stato previsto un ciclo di vita simile e le app possono essere mantenute attive a tempo indeterminato. Con un numero elevato di pagine web in esecuzione, le risorse di sistema critiche come memoria, CPU, batteria e rete possono essere sovrascritte, con conseguente esperienza utente negativa.
Sebbene la piattaforma web abbia da tempo eventi correlati agli stati del ciclo di vita, come load
, unload
e visibilitychange
, questi eventi consentono agli sviluppatori di rispondere solo alle modifiche dello stato del ciclo di vita avviate dall'utente. Affinché il web funzioni in modo affidabile su dispositivi a basso consumo (e in generale per una maggiore consapevolezza delle risorse su tutte le piattaforme), i browser devono poter recuperare e riallocare le risorse di sistema in modo proattivo.
Infatti, oggi i browser adottano già misure attive per risparmiare risorse per le pagine nelle schede in background e molti browser (soprattutto Chrome) vorrebbero fare molto di più per ridurre l'utilizzo complessivo delle risorse.
Il problema è che gli sviluppatori non hanno modo di prepararsi a questi tipi di interventi avviati dal sistema o di sapere nemmeno che si stanno verificando. Ciò significa che i browser devono essere prudenti per non rischiare di danneggiare le pagine web.
L'API Page Lifecycle cerca di risolvere questo problema nel modo seguente:
- Presentazione e standardizzazione del concetto di stati del ciclo di vita sul web.
- Definizione di nuovi stati avviati dal sistema che consentono ai browser di limitare le risorse che possono essere utilizzate dalle schede nascoste o non attive.
- Creazione di nuove API ed eventi che consentano agli sviluppatori web di rispondere alle transizioni verso e da questi nuovi stati avviati dal sistema.
Questa soluzione offre agli sviluppatori web la prevedibilità di cui hanno bisogno per creare applicazioni resilienti agli interventi di sistema e consente ai browser di ottimizzare in modo più aggressivo le risorse di sistema, a beneficio di tutti gli utenti web.
Il resto di questo post introdurrà le nuove funzionalità del ciclo di vita delle pagine ed esploreremo il loro rapporto con tutti gli stati e gli eventi della piattaforma web esistenti. Fornirà inoltre consigli e best practice per i tipi di lavoro che gli sviluppatori dovrebbero (e non dovrebbero) svolgere in ogni stato.
Panoramica degli stati e degli eventi del ciclo di vita della pagina
Tutti gli stati del ciclo di vita della pagina sono discreti e mutuamente esclusivi, il che significa che una pagina può essere in un solo stato alla volta. Inoltre, la maggior parte delle modifiche allo stato del ciclo di vita di una pagina è generalmente osservabile tramite gli eventi DOM (consulta i consigli per gli sviluppatori per ogni stato per le eccezioni).
Forse il modo più semplice per spiegare gli stati del ciclo di vita della pagina, nonché gli eventi che segnalano transizioni tra di loro, è usare un diagramma:
Stati
La tabella seguente illustra in dettaglio ogni stato. Vengono elencati anche i possibili stati che possono precedere e seguire l'evento, nonché gli eventi che gli sviluppatori possono utilizzare per osservare le modifiche.
Stato | Descrizione |
---|---|
Attivi |
Una pagina è nello stato attivo se è visibile e ha il focus sull'input.
Possibili stati precedenti: |
Passivo |
Una pagina è in stato passivo se è visibile e non è impostato sullo stato attivo dell'input.
Possibili stati precedenti:
Possibili stati successivi: |
Nascosto |
Una pagina è in stato nascosto se non è visibile (e non è stata bloccata, eliminata o interrotta).
Possibili stati precedenti:
Possibili stati successivi: |
Congelato |
Quando lo stato è bloccato, il browser sospende l'esecuzione delle
attività
bloccabili nelle
code di attività della pagina finché la pagina non viene sbloccata. Ciò significa che elementi come
i timer e i callback di recupero di JavaScript non vengono eseguiti. Le attività già in esecuzione possono essere completate (in particolare il callback I browser bloccano le pagine per preservare l'utilizzo di CPU/batteria/dati. Lo fanno anche per consentire navigate avanti/indietro più rapide, evitando la necessità di ricaricare completamente la pagina.
Possibili stati precedenti:
Possibili stati successivi: |
Risoluzione |
Una pagina è nello stato terminata dopo l'avvio dell'unload e la cancellazione dalla memoria da parte del browser. Nessuna nuova attività può iniziare in questo stato e le attività in corso potrebbero essere terminate se eseguite troppo a lungo.
Possibili stati precedenti:
Possibili stati successivi: |
Ignorati |
Una pagina è in stato eliminata quando viene scaricata dal browser per risparmiare risorse. In questo stato non è possibile eseguire attività, callback di eventi o JavaScript di alcun tipo, poiché gli scarti si verificano in genere in caso di vincoli di risorse, in cui è impossibile avviare nuovi processi. Nello stato eliminata, la scheda stessa (incluso il titolo della scheda e la favicon) è in genere visibile all'utente anche se la pagina non è più visibile.
Possibili stati precedenti:
Possibili stati successivi: |
Eventi
I browser inviano molti eventi, ma solo una piccola parte di questi segnala una possibile variazione dello stato del ciclo di vita della pagina. La tabella seguente illustra tutti gli eventi relativi al ciclo di vita ed elenca gli stati da e verso i quali possono effettuare la transizione.
Nome | Dettagli |
---|---|
focus
|
Un elemento DOM ha ricevuto lo stato attivo.
Nota: un evento
Possibili stati precedenti:
Possibili stati attuali: |
blur
|
Un elemento DOM ha perso il focus.
Nota: un evento
Possibili stati precedenti:
Possibili stati attuali: |
visibilitychange
|
Il valore di
|
freeze
*
|
La pagina è stata appena bloccata. Qualsiasi attività bloccabile nelle code di attività della pagina non verrà avviata.
Possibili stati precedenti:
Possibili stati correnti: |
resume
*
|
Il browser ha ripreso una pagina congelata.
Possibili stati precedenti:
Possibili stati attuali: |
pageshow
|
È in corso l'esplorazione di una voce della cronologia delle sessioni. Potrebbe trattarsi di un caricamento di pagina nuovo o di una pagina recuperata dalla cache back/forward. Se la pagina è stata recuperata dalla cache back-forward, la proprietà
Possibili stati precedenti: |
pagehide
|
Viene attraversata una voce della cronologia delle sessioni. Se l'utente passa a un'altra pagina e il browser è in grado di aggiungere
la pagina corrente alla cache di navigazione вперед/назад per riutilizzarla in un secondo momento, la proprietà
Possibili stati precedenti:
Possibili stati attuali: |
beforeunload
|
La finestra, il documento e le relative risorse stanno per essere scaricati. Il documento è ancora visibile e l'evento può ancora essere annullato a questo punto.
Importante: l'evento
Possibili stati precedenti:
Possibili stati attuali: |
unload
|
La pagina viene scaricata.
Avviso:
l'utilizzo dell'evento
Possibili stati precedenti:
Possibili stati attuali: |
* Indica un nuovo evento definito dall'API Page Lifecycle
Nuove funzionalità aggiunte in Chrome 68
Il grafico precedente mostra due stati avviati dal sistema anziché dall'utente: congelato e eliminato. Come accennato in precedenza, oggi i browser bloccano e ignorano occasionalmente le schede nascoste (a loro discrezione), ma gli sviluppatori non hanno modo di sapere quando accade.
In Chrome 68, ora gli sviluppatori possono osservare quando una scheda nascosta viene bloccata
e sbloccata ascoltando gli eventi freeze
e resume
su document
.
document.addEventListener('freeze', (event) => {
// The page is now frozen.
});
document.addEventListener('resume', (event) => {
// The page has been unfrozen.
});
A partire da Chrome 68, l'oggetto document
ora include una proprietà
wasDiscarded
su Chrome per computer (il supporto di Android è in fase di monitoraggio in questo problema). Per determinare se una pagina è stata ignorata in una scheda nascosta, puoi controllare il valore di questa proprietà al momento del caricamento della pagina (nota: le pagine ignorate devono essere ricaricate per essere riutilizzate).
if (document.wasDiscarded) {
// Page was previously discarded by the browser while in a hidden tab.
}
Per consigli su ciò che è importante fare negli eventi freeze
e resume
, nonché su come gestire e preparare le pagine che vengono ignorate, consulta i consigli per gli sviluppatori per ogni stato.
Le sezioni successive offrono una panoramica di come queste nuove funzionalità si inseriscono negli stati e negli eventi della piattaforma web esistenti.
Come osservare gli stati del ciclo di vita della pagina nel codice
Negli stati attivo, passivo e nascosto, è possibile eseguire codice JavaScript che determina lo stato attuale del ciclo di vita della pagina dalle API della piattaforma web esistenti.
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
Gli stati congelato e terminato, invece, possono essere rilevati solo nel rispettivo listener di eventi (freeze
e pagehide
) quando lo stato sta cambiando.
Come osservare le modifiche dello stato
Basandoti sulla funzione getState()
definita in precedenza, puoi osservare tutte le modifiche dello stato del ciclo di vita della pagina con il seguente codice.
// 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);
Questo codice esegue tre operazioni:
- Imposta lo stato iniziale utilizzando la funzione
getState()
. - Definisce una funzione che accetta uno stato successivo e, se si verifica una modifica, registra le modifiche dello stato nella console.
- Aggiunge ascoltatori di eventi di acquisizione per tutti gli eventi del ciclo di vita necessari, che a loro volta chiamano
logStateChange()
, passando lo stato successivo.
Una cosa da notare del codice è che tutti i listener di eventi vengono aggiunti
a window
e tutti passano
{capture: true}
.
Ecco alcuni dei motivi:
- Non tutti gli eventi del ciclo di vita della pagina hanno lo stesso target.
pagehide
epageshow
vengono attivati suwindow
;visibilitychange
,freeze
eresume
vengono attivati sudocument
efocus
eblur
vengono attivati sui rispettivi elementi DOM. - La maggior parte di questi eventi non si propaga, il che significa che è impossibile aggiungere listener di eventi non di rilevamento a un elemento antenato comune e osservarli tutti .
- La fase di acquisizione viene eseguita prima delle fasi di destinazione o bubble, quindi l'aggiunta degli ascoltatori aiuta a garantire che vengano eseguiti prima che altro codice possa annullarli.
Consigli per gli sviluppatori per ogni stato
In qualità di sviluppatori, è importante comprendere gli stati del ciclo di vita della pagina e sapere come osservarli nel codice, perché il tipo di lavoro che devi (e non devi) svolgere dipende in gran parte dallo stato della pagina.
Ad esempio, non ha senso mostrare una notifica transitoria all'utente se la pagina è nello stato nascosto. Anche se questo esempio è abbastanza chiaro, esistono altri consigli meno evidenti che vale la pena elencare.
Stato | Consigli per gli sviluppatori |
---|---|
Active |
Lo stato active è il momento più critico per l'utente e quindi il momento più importante per la tua pagina per essere reattiva all'input dell'utente. Qualsiasi attività non UI che potrebbe bloccare il thread principale deve essere ridotta a periodi di inattività o trasferita su un worker web. |
Passive |
Nello stato passivo l'utente non interagisce con la pagina, ma può comunque vederla. Ciò significa che gli aggiornamenti e le animazioni dell'interfaccia utente dovrebbero essere ancora scorrevoli, ma i tempi di questi aggiornamenti sono meno critici. Quando la pagina passa da attiva a passiva, è un buon momento per mantenere lo stato dell'applicazione non salvato. |
Quando la pagina passa da passiva a nascosta, è possibile che l'utente non interagisca di nuovo con la pagina finché non viene ricaricata. La transizione a hidden è spesso anche l'ultima variazione di stato
osservabile in modo affidabile dagli sviluppatori (questo è particolarmente vero su
dispositivi mobili, in quanto gli utenti possono chiudere le schede o l'app del browser stessa e gli eventi
Ciò significa che devi considerare lo stato hidden come la probabile fine della sessione dell'utente. In altre parole, mantieni invariato lo stato dell'applicazione non salvato e invia eventuali dati di analisi non inviati. Inoltre, dovresti interrompere gli aggiornamenti dell'interfaccia utente (poiché non verranno visualizzati dall'utente) e tutte le attività che un utente non vorrebbe eseguire in background. |
|
Frozen |
Nello stato congelato, le attività bloccabili nelle code di attività vengono sospese finché la pagina non viene scongelata, il che potrebbe non accadere mai (ad es. se la pagina viene eliminata). Ciò significa che quando la pagina passa da nascosta a congelata, è essenziale interrompere eventuali timer o eliminare eventuali connessioni che, se congelate, potrebbero influire su altre schede aperte nella stessa origine o sulla capacità del browser di inserire la pagina nella cache di spostamento avanti/indietro. In particolare, è importante che tu:
Devi anche mantenere invariato qualsiasi stato di visualizzazione dinamico (ad es. la posizione di scorrimento
in una visualizzazione elenco infinita) in
Se la pagina passa da congelata a nascosta, puoi riaprire le connessioni chiuse o riavviare eventuali polling che hai interrotto quando la pagina è stata inizialmente bloccata. |
Terminated |
In genere, non è necessario intraprendere alcuna azione quando una pagina passa allo stato Terminata. Poiché le pagine che vengono scaricate a seguito di un'azione dell'utente passano sempre per lo stato hidden prima di entrare nello stato terminated, è nello stato hidden che deve essere eseguita la logica di chiusura della sessione (ad es. la persistenza dello stato dell'applicazione e i report per Analytics). Inoltre, come indicato nei consigli per lo stato hidden, è molto importante che gli sviluppatori comprendano che la transizione allo stato terminated non può essere rilevata in modo affidabile in molti casi (in particolare sui dispositivi mobili), pertanto gli sviluppatori che si basano sugli eventi di interruzione (ad es. |
Discarded |
Lo stato ignorato non è osservabile dagli sviluppatori nel momento in cui una pagina viene eliminata. Questo perché le pagine vengono in genere eliminate in caso di limitazioni delle risorse e sbloccare una pagina solo per consentire l'esecuzione dello script in risposta a un evento di eliminazione non è semplicemente possibile nella maggior parte dei casi. Di conseguenza, devi prepararti alla possibilità di un'eliminazione nel passaggio da nascosto a congelato e poi puoi reagire al ripristino di una pagina eliminata al momento del caricamento della pagina controllando |
Ancora una volta, poiché l'affidabilità e l'ordinamento degli eventi del ciclo di vita non sono implementati in modo coerente in tutti i browser, il modo più semplice per seguire i consigli riportati nella tabella è utilizzare PageLifecycle.js.
API di ciclo di vita legacy da evitare
Se possibile, evita i seguenti eventi.
Evento unload
Molti sviluppatori trattano l'evento unload
come un callback garantito e lo utilizzano come un indicatore di fine sessione per salvare lo stato e inviare i dati di analisi, ma questo approccio è estremamente inaffidabile, soprattutto sui dispositivi mobili. L'evento unload
non viene attivato in molte situazioni di svuotamento tipiche, ad esempio la chiusura di una scheda dal selettore di schede sui dispositivi mobili o la chiusura dell'app del browser dal selettore di app.
Per questo motivo, è sempre meglio fare affidamento sull'evento
visibilitychange
per determinare quando termina una sessione
e considerare lo stato nascosto come
l'ultimo momento affidabile per salvare i dati dell'app e dell'utente.
Inoltre, la sola presenza di un gestore di eventi unload
registrato (tramite onunload
o addEventListener()
) può impedire ai browser di inserire le pagine nella cache back-forward per caricamenti avanti e indietro più rapidi.
In tutti i browser moderni, è consigliabile utilizzare sempre l'evento
pagehide
per rilevare possibili svuotamenti della pagina (noto anche come stato
terminato) anziché l'evento unload
. Se
devi supportare le versioni di Internet Explorer 10 e precedenti, devi rilevare
l'evento pagehide
e utilizzare unload
solo se il browser non supporta
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.
});
L'evento beforeunload
L'evento beforeunload
presenta un problema simile a quello dell'evento unload
, in quanto,
in passato, la presenza di un evento beforeunload
poteva impedire alle pagine di essere
idonee per la cache back/forward. I browser moderni non hanno questa limitazione. Anche se alcuni browser, in via precauzionale, non attivano
l'evento beforeunload
quando si tenta di inserire una pagina nella cache
back/forward, il che significa che l'evento non è affidabile come indicatore di fine sessione.
Inoltre, alcuni browser (incluso Chrome)
richiedono un'interazione dell'utente sulla pagina prima di consentire l'attivazione dell'evento beforeunload
, compromettendo ulteriormente la sua affidabilità.
Una differenza tra beforeunload
e unload
è che esistono
utilizzi legittimi di beforeunload
. Ad esempio, quando vuoi avvisare l'utente che ha modifiche non salvate che perderà se continua a scaricare la pagina.
Poiché esistono motivi validi per utilizzare beforeunload
, ti consigliamo di
solo aggiungere ascoltatori beforeunload
quando un utente ha modifiche non salvate, per poi
rimuoverli immediatamente dopo il salvataggio.
In altre parole, non fare così (poiché aggiunge un ascoltatore beforeunload
incondizionatamente):
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);
}
});
Esegui invece questa operazione (poiché aggiunge l'ascoltatore beforeunload
solo quando è necessario e lo rimuove quando non è necessario):
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);
});
Domande frequenti
Perché non esiste uno stato "In caricamento"?
L'API Page Lifecycle definisce gli stati come discreti e reciprocamente esclusivi. Poiché una pagina può essere caricata in stato attivo, passivo o nascosto, e poiché può cambiare stato (o persino essere terminata) prima del termine del caricamento, uno stato di caricamento separato non ha senso all'interno di questo paradigma.
La mia pagina svolge una funzione importante quando è nascosta. Come posso evitare che venga bloccata o eliminata?
Esistono molti motivi legittimi per cui le pagine web non devono essere bloccate durante l'esecuzione in stato nascosto. L'esempio più ovvio è un'app che riproduce musica.
Esistono anche situazioni in cui sarebbe rischioso per Chrome eliminare una pagina, ad esempio se contiene un modulo con input utente non inviati o se ha un gestore beforeunload
che avvisa quando la pagina viene scaricata.
Per il momento, Chrome sarà prudente quando eliminerà le pagine e lo farà solo quando avrà la certezza che non influirà sugli utenti. Ad esempio, le pagine per le quali è stato osservato che eseguono una delle seguenti operazioni nello stato nascosto non verranno eliminate, a meno che non si verifichino vincoli di risorse estremi:
- Riproduzione audio
- Utilizzo di WebRTC
- Aggiornamento del titolo o della favicon della tabella
- Visualizzazione di avvisi
- Invio di notifiche push
Per le funzionalità dell'elenco corrente utilizzate per determinare se una scheda può essere messa in pausa o eliminata in sicurezza, consulta Euristiche per la messa in pausa ed l'eliminazione in Chrome.
Che cos'è la cache back-forward?
La cache back-forward è un termine utilizzato per descrivere un'ottimizzazione della navigazione implementata da alcuni browser che consente di utilizzare più velocemente i pulsanti Indietro e Avanti.
Quando un utente esce da una pagina, questi browser bloccano una versione della pagina in modo che possa essere ripresa rapidamente nel caso in cui l'utente torni indietro utilizzando i pulsanti Indietro o Avanti. Ricorda che l'aggiunta di un gestore eventi unload
impedisce questa ottimizzazione.
A tutti gli effetti, questo blocco è funzionalmente uguale a quello eseguito dai browser per risparmiare CPU/batteria; per questo motivo, è considerato parte dello stato di ciclo di vita congelato.
Se non posso eseguire API asincrone negli stati bloccati o terminati, come posso salvare i dati in IndexedDB?
Negli stati bloccati e terminati, le attività bloccabili nelle code di attività di una pagina vengono sospese, il che significa che le API asincrone e basate su callback come IndexedDB non possono essere utilizzate in modo affidabile.
In futuro, aggiungeremo un metodo commit()
agli oggetti IDBTransaction
, che offrirà agli sviluppatori un modo per eseguire transazioni di sola scrittura che non richiedono callback. In altre parole, se lo sviluppatore sta solo scrivendo
dati in IndexedDB e non esegue una transazione complessa composta da letture
e scritture, il metodo commit()
potrà terminare prima che le code di attività vengano
sospese (supponendo che il database IndexedDB sia già aperto).
Tuttavia, per il codice che deve funzionare oggi, gli sviluppatori hanno due opzioni:
- Utilizza l'archiviazione di sessione: l'archiviazione di sessione è sincrona e viene mantenuta in tutte le pagine eliminate.
- Utilizza IndexedDB dal tuo worker di servizio: un worker di servizio può memorizzare i dati in IndexedDB dopo la chiusura o l'eliminazione della pagina. Nell'ascoltatore di eventi
freeze
opagehide
puoi inviare dati al tuo service worker tramitepostMessage()
, e il service worker può gestire il salvataggio dei dati.
Testare l'app negli stati di sospensione e di eliminazione
Per testare il comportamento della tua app negli stati di sospensione e di eliminazione, puoi visitare
chrome://discards
per bloccare o eliminare effettivamente le tue
schede aperte.
In questo modo puoi assicurarti che la pagina gestisca correttamente gli eventi freeze
e resume
nonché il flag document.wasDiscarded
quando le pagine vengono ricaricate dopo un abbandono.
Riepilogo
Gli sviluppatori che vogliono rispettare le risorse di sistema dei dispositivi degli utenti devono creare le loro app tenendo presente gli stati del ciclo di vita delle pagine. È fondamentale che le pagine web non consumino risorse di sistema eccessive in situazioni che l'utente non si aspetta
Più sviluppatori iniziano a implementare le nuove API Page Lifecycle, più sicuro sarà per i browser bloccare e ignorare le pagine che non vengono utilizzate. Ciò significa che i browser consumeranno meno memoria, CPU, batteria e risorse di rete, il che è un vantaggio per gli utenti.