Metodi del Ciclo di Vita
Uso
I Componenti e i nodi del DOM virtuale possono definire dei metodi del ciclo di vita, detti anche hook, che vengono eseguiti in diverse fasi del ciclo di vita di un elemento DOM.
// Esempio di hook in un componente
var ComponentWithHook = {
oninit: function (vnode) {
console.log('Inizializzazione del componente');
},
view: function () {
return 'hello';
},
};
// Esempio di hook in un vnode
function initializeVnode() {
console.log('Inizializzazione del vnode');
}
m(ComponentWithHook, { oninit: initializeVnode });
Tutti i metodi del ciclo di vita ricevono il vnode come primo argomento e hanno vnode.state
come contesto (this
).
Questi hook non vengono eseguiti se il DOM viene modificato al di fuori del controllo di Mithril.
Il Ciclo di Vita dell'Elemento DOM
Un elemento DOM viene generalmente creato e aggiunto al documento. Successivamente, i suoi attributi o nodi figlio possono essere aggiornati in risposta a eventi dell'interfaccia utente e modifiche ai dati. Infine, l'elemento può essere rimosso dal documento.
Dopo la rimozione, un elemento può essere temporaneamente conservato in un pool di memoria. L'elemento in pool può essere riutilizzato in un aggiornamento successivo (in un processo chiamato riciclo del DOM). Il riciclo di un elemento evita il costo prestazionale della ricreazione di una copia di un elemento esistente.
oninit
oninit(vnode)
viene eseguito prima che un vnode venga elaborato dal motore del DOM virtuale. oninit
viene eseguito prima che il suo elemento DOM venga collegato al documento e sui vnode padre prima dei loro figli, ma non offre alcuna garanzia sull'esistenza di elementi DOM genitori o figli. Non si dovrebbe mai accedere a vnode.dom
all'interno del metodo oninit
.
Questo hook non viene invocato quando un elemento viene aggiornato, ma viene invocato se un elemento viene riciclato.
Come in altri hook, la parola chiave this
nel callback oninit
punta a vnode.state
.
L'hook oninit
è utile per inizializzare lo stato di un componente in base agli argomenti passati tramite vnode.attrs
o vnode.children
.
function ComponentWithState() {
var initialData;
return {
oninit: function (vnode) {
initialData = vnode.attrs.data;
},
view: function (vnode) {
return [
// mostra i dati al momento dell'inizializzazione:
m('div', 'Initial: ' + initialData),
// visualizza i dati correnti:
m('div', 'Current: ' + vnode.attrs.data),
];
},
};
}
m(ComponentWithState, { data: 'Hello' });
Non si dovrebbero modificare i dati del modello in modo sincrono all'interno di questo metodo. Poiché oninit
non fornisce garanzie sullo stato degli altri elementi, le modifiche al modello apportate in questo metodo potrebbero non riflettersi in tutte le parti dell'interfaccia utente fino al ciclo di rendering successivo.
oncreate
oncreate(vnode)
viene eseguito dopo che un elemento DOM viene creato e collegato al documento. oncreate
viene eseguito alla fine del ciclo di rendering, quindi è sicuro leggere i valori di layout come vnode.dom.offsetHeight
e vnode.dom.getBoundingClientRect()
all'interno di questo metodo.
Questo hook non viene invocato quando un elemento viene aggiornato.
Come in altri hook, la parola chiave this
nel callback oncreate
punta a vnode.state
. Gli elementi DOM con vnode che definiscono un hook oncreate
non vengono riciclati.
L'hook oncreate
è utile per leggere i valori di layout che potrebbero provocare un ridisegno, avviare animazioni e inizializzare librerie di terze parti che richiedono un riferimento all'elemento DOM.
var HeightReporter = {
oncreate: function (vnode) {
console.log('Inizializzato con altezza di: ', vnode.dom.offsetHeight);
},
view: function () {},
};
m(HeightReporter, { data: 'Hello' });
Non si dovrebbero modificare i dati del modello in modo sincrono all'interno di questo metodo. Poiché oncreate
viene eseguito alla fine del ciclo di rendering, le modifiche al modello apportate in questo metodo non si rifletteranno nell'interfaccia utente fino al ciclo di rendering successivo.
onupdate
onupdate(vnode)
viene eseguito dopo che un elemento DOM viene aggiornato, mentre è collegato al documento. onupdate
viene eseguito alla fine del ciclo di rendering, quindi è sicuro leggere i valori di layout come vnode.dom.offsetHeight
e vnode.dom.getBoundingClientRect()
all'interno di questo metodo.
Questo hook viene eseguito solo se l'elemento esisteva nel ciclo di rendering precedente. Non viene invocato quando un elemento viene creato o quando viene riciclato.
Gli elementi DOM con vnode che definiscono un hook onupdate
non vengono riciclati.
L'hook onupdate
è utile per leggere i valori di layout che potrebbero provocare un ridisegno e per aggiornare dinamicamente lo stato dell'interfaccia utente nelle librerie di terze parti dopo la modifica dei dati del modello.
function RedrawReporter() {
var count = 0;
return {
onupdate: function () {
console.log('Ridisegni effettuati finora: ', ++count);
},
view: function () {},
};
}
m(RedrawReporter, { data: 'Hello' });
onbeforeremove
onbeforeremove(vnode)
viene eseguito prima che un elemento DOM venga rimosso dal documento. Se viene restituita una Promise, Mithril.js scollegherà l'elemento DOM solo dopo il completamento della Promise.
Questo hook viene eseguito solo sull'elemento DOM che perde il suo parentNode
, ma non sui suoi elementi figlio.
Come in altri hook, la parola chiave this
nel callback onbeforeremove
punta a vnode.state
. Gli elementi DOM con vnode che definiscono un hook onbeforeremove
non vengono riciclati.
var Fader = {
onbeforeremove: function (vnode) {
vnode.dom.classList.add('fade-out');
return new Promise(function (resolve) {
setTimeout(resolve, 1000);
});
},
view: function () {
return m('div', 'Bye');
},
};
onremove
onremove(vnode)
viene eseguito prima che un elemento DOM venga rimosso dal documento. Se è definito anche un hook onbeforeremove
, l'hook onremove
viene eseguito dopo il completamento della Promise restituita da onbeforeremove
.
Questo hook viene eseguito su qualsiasi elemento rimosso dal documento, indipendentemente dal fatto che sia stato scollegato direttamente dal suo elemento padre o che sia un figlio di un altro elemento scollegato.
Come in altri hook, la parola chiave this
nel callback onremove
punta a vnode.state
. Gli elementi DOM con vnode che definiscono un hook onremove
non vengono riciclati.
L'hook onremove
è utile per eseguire operazioni di pulizia.
function Timer() {
var timeout = setTimeout(function () {
console.log('Timeout scaduto');
}, 1000);
return {
onremove: function () {
clearTimeout(timeout);
},
view: function () {},
};
}
onbeforeupdate
L'hook onbeforeupdate(vnode, old)
viene eseguito prima che un vnode venga confrontato durante un aggiornamento. Se questa funzione è definita e restituisce false
, Mithril.js impedisce che venga eseguito il diffing sul vnode e, di conseguenza, sui suoi figli.
Questo hook di per sé non impedisce la generazione di un sottoalbero del DOM virtuale, a meno che quest'ultimo non sia incapsulato all'interno di un componente.
Come in altri hook, la parola chiave this
nel callback onbeforeupdate
punta a vnode.state
.
Questo hook è utile per ridurre la latenza degli aggiornamenti in presenza di un albero DOM particolarmente grande.
Evitare gli Anti-pattern
Sebbene Mithril.js sia flessibile, alcuni modelli di codice sono sconsigliati:
Evitare le Ottimizzazioni Premature
Si dovrebbe utilizzare onbeforeupdate
per evitare il diffing solo come ultima risorsa. Evitare di utilizzarlo a meno che non si riscontri un problema di prestazioni significativo.
In genere, i problemi di prestazioni risolvibili tramite onbeforeupdate
si riducono a un singolo array di grandi dimensioni. In questo contesto, "grande" si riferisce a qualsiasi array contenente un numero elevato di nodi, sia in una struttura ampia (come la famigerata tabella da 5000 righe) sia in un albero profondo e denso.
In caso di problemi di prestazioni, valutare innanzitutto se l'interfaccia utente offre una buona esperienza utente e, in caso contrario, modificarla. Ad esempio, è improbabile che un utente debba esaminare 5000 righe di dati tabellari non elaborati; è invece più probabile che trovi più utile una funzione di ricerca che restituisca solo i primi risultati più pertinenti.
Se una soluzione basata sul design non è praticabile e si deve ottimizzare un'interfaccia utente con un numero elevato di elementi DOM, applicare onbeforeupdate
al nodo padre dell'array più grande e rivalutare le prestazioni. Nella maggior parte dei casi, un singolo controllo dovrebbe essere sufficiente. In caso contrario, ripetere il processo, ma prestare sempre più attenzione a ogni nuova dichiarazione onbeforeupdate
.
Un numero eccessivo di onbeforeupdate
indica potenziali problemi di definizione delle priorità nel flusso di lavoro di progettazione.
Evitare di applicare questa ottimizzazione ad altre aree dell'applicazione "per sicurezza". Ricordare che, in generale, una maggiore quantità di codice comporta costi di manutenzione più elevati e che i bug relativi a onbeforeupdate
possono essere particolarmente difficili da risolvere se si fa affidamento sull'identità degli oggetti per i controlli condizionali.
Ancora una volta, l'hook onbeforeupdate
va utilizzato solo come ultima risorsa.