m(selector, attributes, children)
Descrizione
Rappresenta un elemento HTML all'interno di una vista Mithril.js.
m('div.foo', { style: { color: 'red' } }, 'hello');
// viene renderizzato come questo HTML:
// <div class="foo" style="color: red">hello</div>
È possibile utilizzare anche una sintassi simile all'HTML chiamata JSX, usando Babel per convertirla in chiamate iper-script equivalenti. Questo è equivalente all'esempio precedente.
<div class="foo" style="color: red">
hello
</div>
Firma
vnode = m(selector, attrs, children)
Parametro | Tipo | Richiesto | Descrizione |
---|---|---|---|
selector | String|Object|Function | Sì | Un selettore CSS o un componente |
attrs | Object | No | Attributi HTML o proprietà dell'elemento |
children | Array<Vnode>|String|Number|Boolean | No | Vnode figli. Può essere scritto come argomenti splat |
restituisce | Vnode | Un vnode |
Come funziona
Mithril.js fornisce una funzione iper-script m()
che consente di esprimere qualsiasi struttura HTML usando la sintassi JavaScript. Accetta una stringa selector
(obbligatoria), un oggetto attrs
(opzionale) e un array children
(opzionale).
m('div', { id: 'box' }, 'hello');
// viene renderizzato come questo HTML:
// <div id="box">hello</div>
La funzione m()
non restituisce direttamente un elemento DOM. Restituisce invece un nodo DOM virtuale, o vnode, un oggetto JavaScript che rappresenta l'elemento DOM da creare.
// un vnode
var vnode = {
tag: 'div',
attrs: { id: 'box' },
children: [
/*...*/
],
};
Per trasformare un vnode in un elemento DOM reale, usa la funzione m.render()
:
m.render(document.body, m('br')); // inserisce un <br> in <body>
Chiamare m.render()
più volte non ricrea l'albero DOM da zero a ogni chiamata. Invece, ogni chiamata apporta modifiche all'albero DOM solo se strettamente necessario per riflettere l'albero DOM virtuale passato. Questo comportamento è preferibile perché ricreare il DOM da zero è un'operazione costosa e causa problemi come la perdita del focus degli input. Al contrario, aggiornare il DOM solo dove necessario è molto più veloce e semplifica la gestione di interfacce utente complesse che gestiscono molteplici interazioni utente.
Flessibilità
La funzione m()
è sia polimorfica che variadica. In altre parole, è molto flessibile riguardo ai parametri di input accettati:
// tag semplice
m('div'); // <div></div>
// attributi e children sono opzionali
m('a', { id: 'b' }); // <a id="b"></a>
m('span', 'hello'); // <span>hello</span>
// tag con nodi figli
m('ul', [
// <ul>
m('li', 'hello'), // <li>hello</li>
m('li', 'world'), // <li>world</li>
]); // </ul>
// array è opzionale
m(
'ul', // <ul>
m('li', 'hello'), // <li>hello</li>
m('li', 'world') // <li>world</li>
); // </ul>
Selettori CSS
Il primo argomento di m()
può essere qualsiasi selettore CSS che descrive un elemento HTML. Accetta qualsiasi combinazione CSS valida di #
(id), .
(classe) e []
(attributo).
m('div#hello');
// <div id="hello"></div>
m('section.container');
// <section class="container"></section>
m('input[type=text][placeholder=Name]');
// <input type="text" placeholder="Name" />
m("a#exit.external[href='https://example.com']", 'Leave');
// <a id="exit" class="external" href="https://example.com">Leave</a>
Se si omette il nome del tag, Mithril.js assume un tag div
.
m('.box.box-bordered'); // <div class="box box-bordered"></div>
In genere, si raccomanda di usare i selettori CSS per gli attributi statici (cioè attributi il cui valore non cambia) e passare un oggetto attrs
per i valori degli attributi dinamici.
var currentURL = '/';
m(
'a.link[href=/]',
{
class: currentURL === '/' ? 'selected' : '',
},
'Home'
);
// viene renderizzato come questo HTML:
// <a href="/" class="link selected">Home</a>
Attributi passati come secondo argomento
È possibile passare attributi, proprietà, eventi e hook del ciclo di vita nel secondo argomento opzionale (per i dettagli, vedere le sezioni successive).
m("button", {
class: "my-button",
onclick: function() {/* ... */},
oncreate: function() {/* ... */}
})
Se il valore di un attributo è null
o undefined
, viene trattato come se l'attributo fosse assente.
Se ci sono nomi di classe sia nel primo che nel secondo argomento di m()
, vengono uniti come previsto. Se il valore della classe nel secondo argomento è null
o undefined
, viene ignorato.
Se un altro attributo è presente sia nel primo che nel secondo argomento, il secondo ha la precedenza anche se è null
o undefined
.
Attributi DOM
Mithril.js usa sia l'API JavaScript che l'API DOM (setAttribute
) per impostare gli attributi. Questo significa che è possibile usare entrambe le sintassi per riferirsi agli attributi.
Per esempio, nell'API JavaScript, l'attributo readonly
è chiamato element.readOnly
(nota la maiuscola). In Mithril.js, sono supportati tutti i seguenti:
m('input', { readonly: true }); // minuscolo
m('input', { readOnly: true }); // maiuscolo
m('input[readonly]');
m('input[readOnly]');
Questo include anche elementi personalizzati. Ad esempio, è possibile usare A-Frame con Mithril.js senza problemi.
m('a-scene', [
m('a-box', {
position: '-1 0.5 -3',
rotation: '0 45 0',
color: '#4CC3D9',
}),
m('a-sphere', {
position: '0 1.25 -5',
radius: '1.25',
color: '#EF2D5E',
}),
m('a-cylinder', {
position: '1 0.75 -3',
radius: '0.5',
height: '1.5',
color: '#FFC65D',
}),
m('a-plane', {
position: '0 0 -4',
rotation: '-90 0 0',
width: '4',
height: '4',
color: '#7BC8A4',
}),
m('a-sky', {
color: '#ECECEC',
}),
]);
Per gli elementi personalizzati, Mithril.js non converte automaticamente le proprietà in stringhe, nel caso in cui siano oggetti, numeri o altri valori non stringa. Quindi, supponendo di avere un elemento personalizzato my-special-element
che ha una proprietà getter/setter array elem.whitelist
, è possibile fare questo e funzionerà come previsto:
m('my-special-element', {
whitelist: [
'https://example.com',
'https://neverssl.com',
'https://google.com',
],
});
Se si hanno classi o ID per questi elementi, le abbreviazioni funzionano come previsto. Per prendere un altro esempio di A-Frame:
// Questi due sono equivalenti
m('a-entity#player');
m('a-entity', { id: 'player' });
Si noti che tutte le proprietà con semantica speciale, come gli attributi del ciclo di vita, i gestori onevent
, le key
, class
e style
, sono trattate come per i normali elementi HTML.
Attributo Style
Mithril.js supporta sia stringhe che oggetti come valori style
validi. In altre parole, sono supportati tutti i seguenti:
m('div', { style: 'background:red;' });
m('div', { style: { background: 'red' } });
m('div[style=background:red]');
Usare una stringa come style
sovrascriverà tutti gli stili inline dell'elemento a ogni ridisegno, non solo le regole CSS modificate.
È possibile usare sia i nomi delle proprietà CSS con trattino (come background-color
) sia i nomi delle proprietà style
DOM in camel case (come backgroundColor
). È anche possibile definire proprietà personalizzate CSS, se il browser le supporta.
Mithril.js non tenta di aggiungere unità ai valori numerici. Li converte semplicemente in stringhe.
Eventi
Mithril.js supporta il binding di gestori di eventi per tutti gli eventi DOM, inclusi gli eventi le cui specifiche non definiscono una proprietà on${event}
, come touchstart
.
function doSomething(e) {
console.log(e);
}
m('div', { onclick: doSomething });
Mithril.js accetta funzioni e oggetti EventListener. Quindi anche questo funzionerà:
var clickListener = {
handleEvent: function (e) {
console.log(e);
},
};
m('div', { onclick: clickListener });
Per impostazione predefinita, quando un evento collegato con iper-script viene attivato, Mithril.js esegue un ridisegno automatico dopo che la callback dell'evento è stata eseguita (a condizione che si stia usando m.mount
o m.route
invece di m.render
direttamente). È possibile disabilitare il ridisegno automatico specificamente per un singolo evento impostando e.redraw = false
:
m('div', {
onclick: function (e) {
// Impedisci il ridisegno automatico
e.redraw = false;
},
});
Proprietà
Mithril.js supporta la funzionalità DOM accessibile tramite proprietà come le proprietà selectedIndex
e value
di <select>
.
m('select', { selectedIndex: 0 }, [
m('option', 'Option A'),
m('option', 'Option B'),
]);
Componenti
I Componenti consentono di incapsulare la logica in un'unità e usarla come se fosse un elemento. Sono la base per creare applicazioni grandi e scalabili.
Un componente è qualsiasi oggetto JavaScript che contiene un metodo view
. Per usare un componente, passarlo come primo argomento a m()
invece di una stringa selettore CSS. È possibile passare argomenti al componente definendo attributi e children, come mostrato nell'esempio qui sotto.
// definisci un componente
var Greeter = {
view: function (vnode) {
return m('div', vnode.attrs, ['Hello ', vnode.children]);
},
};
// usalo
m(Greeter, { style: 'color:red;' }, 'world');
// viene renderizzato come questo HTML:
// <div style="color:red;">Hello world</div>
Per maggiori informazioni sui componenti, vedere la pagina dei componenti.
Metodi del ciclo di vita
I vnode e i componenti possono avere metodi del ciclo di vita (noti anche come hooks), che vengono chiamati in vari punti durante la durata di un elemento DOM. I metodi del ciclo di vita supportati da Mithril.js sono: oninit
, oncreate
, onupdate
, onbeforeremove
, onremove
e onbeforeupdate
.
I metodi del ciclo di vita sono definiti nello stesso modo dei gestori di eventi DOM, ma ricevono il vnode come argomento, invece di un oggetto Event:
function initialize(vnode) {
console.log(vnode);
}
m('div', { oninit: initialize });
Hook | Descrizione |
---|---|
oninit(vnode) | Viene eseguito prima che un vnode sia renderizzato in un elemento DOM reale. |
oncreate(vnode) | Viene eseguito dopo che un vnode viene aggiunto al DOM. |
onupdate(vnode) | Viene eseguito ogni volta che si verifica un ridisegno mentre l'elemento DOM è collegato al documento. |
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 che la Promise sarà stata completata. |
onremove(vnode) | Viene eseguito prima che un elemento DOM venga rimosso dal documento. Se è definito un hook onbeforeremove , onremove viene chiamato dopo che done è stato chiamato. Questo metodo viene attivato sull'elemento che viene scollegato dal suo elemento padre e su tutti i suoi figli. |
onbeforeupdate(vnode, old) | Viene eseguito prima di onupdate e, se restituisce false , impedisce un confronto differenziale (diff) per l'elemento e tutti i suoi figli. |
Per maggiori informazioni sui metodi del ciclo di vita, vedere la pagina dei metodi del ciclo di vita.
Key
I vnode in una lista possono avere un attributo speciale chiamato key
, che può essere usato per gestire l'identità dell'elemento DOM quando i dati del modello che generano la lista di vnode cambiano.
In genere, key
dovrebbe essere il campo identificatore univoco degli oggetti nell'array di dati.
var users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Mary' },
];
function userInputs(users) {
return users.map(function (u) {
return m('input', { key: u.id }, u.name);
});
}
m.render(document.body, userInputs(users));
L'uso di una key fa sì che, se l'array users
viene mescolato e la vista viene ri-renderizzata, gli input verranno mescolati nello stesso ordine, mantenendo lo stato corretto del focus e del DOM.
Per maggiori informazioni sulle key, vedere la pagina delle key.
SVG e MathML
Mithril.js supporta completamente SVG. Anche Xlink è supportato, ma a differenza delle versioni pre-v1.0 di Mithril.js, deve avere il namespace definito esplicitamente:
m('svg', [m("image[xlink:href='image.gif']")]);
Anche MathML è completamente supportato.
Rendere i template dinamici
Dato che i vnode nidificati sono semplici espressioni JavaScript, è possibile usare le funzionalità JavaScript per manipolarli.
Testo dinamico
var user = { name: 'John' };
m('.name', user.name); // <div class="name">John</div>
Loop
Usare i metodi Array
come map
per iterare su liste di dati.
var users = [{ name: 'John' }, { name: 'Mary' }];
m(
'ul',
users.map(function (u) {
// <ul>
return m('li', u.name); // <li>John</li>
// <li>Mary</li>
})
); // </ul>
// ES6+:
// m("ul", users.map(u =>
// m("li", u.name)
// ))
Condizionali
Usare l'operatore ternario per impostare condizionalmente il contenuto di una view.
var isError = false;
m('div', isError ? 'An error occurred' : 'Saved'); // <div>Saved</div>
Non è possibile usare istruzioni JavaScript come if
o for
all'interno di espressioni JavaScript. È preferibile evitare di usare del tutto queste istruzioni e invece, usare esclusivamente i costrutti sopra per mantenere la struttura dei template lineare e dichiarativa.
Convertire HTML
In Mithril.js, l'HTML ben formato è JSX valido. È richiesto poco sforzo, oltre al copia-incolla, per integrare un file HTML prodotto indipendentemente in un progetto che usa JSX.
Quando si usa iper-script, è necessario convertire l'HTML in sintassi di iper-script prima che il codice possa essere eseguito. Per facilitare questo, è possibile usare il convertitore HTML-to-Mithril-template.
Evitare Anti-pattern
Nonostante Mithril.js sia flessibile, alcuni schemi di codice sono sconsigliati:
Evitare selettori dinamici
Elementi DOM diversi hanno attributi diversi e spesso comportamenti diversi. Rendere un selettore configurabile può esporre i dettagli implementativi di un componente al di fuori della sua unità, violandone l'incapsulamento.
// EVITARE
var BadInput = {
view: function (vnode) {
return m('div', [m('label'), m(vnode.attrs.type||'input')]);
},
};
Invece di rendere i selettori dinamici, si consiglia di implementare esplicitamente ogni possibilità valida oppure di rifattorizzare la parte variabile del codice.
// PREFERIRE codice esplicito
var BetterInput = {
view: function (vnode) {
return m('div', [m('label', vnode.attrs.title), m('input')]);
},
};
var BetterSelect = {
view: function (vnode) {
return m('div', [m('label', vnode.attrs.title), m('select')]);
},
};
// PREFERIRE rifattorizzare la variabilità
var BetterLabeledComponent = {
view: function (vnode) {
return m('div', [m('label', vnode.attrs.title), vnode.children]);
},
};
Evitare di creare vnode al di fuori delle view
Quando un ridisegno incontra un vnode che è strettamente uguale a quello nel rendering precedente, verrà saltato e il suo contenuto non verrà aggiornato. Sebbene possa sembrare un'opportunità per ottimizzare le prestazioni, è sconsigliato perché impedisce cambiamenti dinamici nell'albero di quel nodo, causando effetti collaterali come il mancato avvio dei metodi del ciclo di vita a valle durante il ridisegno. In questo senso, i vnode di Mithril.js sono immutabili: i nuovi vnode vengono confrontati con quelli vecchi; le mutazioni ai vnode non vengono mantenute.
La documentazione del componente contiene maggiori dettagli e un esempio di questo anti-pattern.