m(selector, attributes, children)
Opis funkcji
Reprezentuje element HTML w widoku Mithril.js.
m('div.foo', { style: { color: 'red' } }, 'hello');
// renderuje do następującego HTML:
// <div class="foo" style="color: red">hello</div>
Możesz również użyć składni podobnej do HTML, zwanej JSX, która jest konwertowana na równoważne wywołania hyperscript za pomocą Babel. Poniższy przykład jest równoważny powyższemu.
<div class="foo" style="color: red">
hello
</div>
Sygnatura
vnode = m(selector, attrs, children)
Argument | Typ | Wymagany | Opis |
---|---|---|---|
selector | String|Object|Function | Tak | Selektor CSS lub komponent |
attrs | Object | Nie | Atrybuty HTML lub właściwości elementu |
children | Array<Vnode>|String|Number|Boolean | Nie | Węzły potomne vnode. Mogą być zapisane jako argumenty splat |
zwraca | Vnode | Vnode |
Jak to działa
Mithril.js oferuje funkcję hyperscript m()
, która pozwala wyrazić dowolną strukturę HTML za pomocą składni JavaScript. Przyjmuje ona ciąg selector
(wymagany), obiekt attrs
(opcjonalny) i tablicę children
(opcjonalną).
m('div', { id: 'box' }, 'hello');
// renderuje do następującego HTML:
// <div id="box">hello</div>
Funkcja m()
nie zwraca bezpośrednio elementu DOM. Zamiast tego zwraca wirtualny węzeł DOM, czyli vnode, który jest obiektem JavaScript reprezentującym element DOM, który ma zostać utworzony.
// vnode
var vnode = {
tag: 'div',
attrs: { id: 'box' },
children: [
/*...*/
],
};
Aby przekształcić vnode w rzeczywisty element DOM, użyj funkcji m.render()
:
m.render(document.body, m('br')); // umieszcza <br> w <body>
Wielokrotne wywoływanie m.render()
nie powoduje ponownego tworzenia drzewa DOM od zera za każdym razem. Zamiast tego, każde wywołanie wprowadza zmiany w drzewie DOM tylko wtedy, gdy jest to absolutnie konieczne, aby odzwierciedlić wirtualne drzewo DOM przekazane do wywołania. Takie zachowanie jest pożądane, ponieważ ponowne tworzenie DOM od zera jest bardzo kosztowne i powoduje problemy, takie jak utrata fokusu w polu tekstowym, między innymi. Natomiast aktualizacja DOM tylko tam, gdzie to konieczne, jest stosunkowo znacznie szybsza i ułatwia utrzymanie złożonych interfejsów użytkownika, które obsługują wiele scenariuszy użytkownika.
Elastyczność
Funkcja m()
jest polimorficzna i wariadyczna. Innymi słowy, jest bardzo elastyczna w kwestii akceptowanych parametrów wejściowych:
// prosty tag
m('div'); // <div></div>
// atrybuty i dzieci są opcjonalne
m('a', { id: 'b' }); // <a id="b"></a>
m('span', 'hello'); // <span>hello</span>
// tag z węzłami potomnymi
m('ul', [
// <ul>
m('li', 'hello'), // <li>hello</li>
m('li', 'world'), // <li>world</li>
]); // </ul>
// tablica jest opcjonalna
m(
'ul', // <ul>
m('li', 'hello'), // <li>hello</li>
m('li', 'world') // <li>world</li>
); // </ul>
Selektory CSS
Pierwszym argumentem m()
może być dowolny selektor CSS, który może opisywać element HTML. Akceptuje on dowolne prawidłowe kombinacje składni CSS #
(id), .
(klasa) i []
(atrybut).
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>
Jeśli pominiesz nazwę tagu, Mithril.js zakłada tag div
.
m('.box.box-bordered'); // <div class="box box-bordered"></div>
Zazwyczaj zaleca się stosowanie selektorów CSS dla atrybutów statycznych (tj. atrybutów, których wartość się nie zmienia) i przekazywanie obiektu atrybutów dla dynamicznych wartości atrybutów.
var currentURL = '/';
m(
'a.link[href=/]',
{
class: currentURL === '/' ? 'selected' : '',
},
'Home'
);
// renderuje do następującego HTML:
// <a href="/" class="link selected">Home</a>
Atrybuty przekazywane jako drugi argument
Możesz przekazywać atrybuty, właściwości, zdarzenia i hooki cyklu życia w drugim, opcjonalnym argumencie (szczegóły w następnych sekcjach).
m("button", {
class: "my-button",
onclick: function() {/* ... */},
oncreate: function() {/* ... */}
})
Jeśli wartością atrybutu jest null
lub undefined
, jest on traktowany tak, jakby atrybut był nieobecny.
Nazwy klas z pierwszego i drugiego argumentu m()
są łączone. Jeśli wartością klasy w drugim argumencie jest null
lub undefined
, jest ona ignorowana.
Jeśli ten sam atrybut jest obecny zarówno w pierwszym, jak i drugim argumencie, drugi ma pierwszeństwo, nawet jeśli jest null
lub undefined
.
Atrybuty DOM
Mithril.js używa zarówno JavaScript API, jak i DOM API (setAttribute
) do obsługi atrybutów. Oznacza to, że możesz używać obu składni do odwoływania się do atrybutów.
Na przykład, w JavaScript API, atrybut readonly
nazywa się element.readOnly
(zauważ wielką literę). W Mithril.js obsługiwane są wszystkie poniższe:
m('input', { readonly: true }); // małe litery
m('input', { readOnly: true }); // wielkie litery
m('input[readonly]');
m('input[readOnly]');
Dotyczy to nawet elementów niestandardowych. Na przykład, możesz użyć A-Frame w Mithril.js, bez problemu!
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',
}),
]);
W przypadku elementów niestandardowych nie następuje automatyczne przekształcanie właściwości w ciągi znaków, na wypadek gdyby były obiektami, liczbami lub jakąś inną wartością niebędącą ciągiem znaków. Zakładając więc, że masz jakiś element niestandardowy my-special-element
, który ma właściwość getter/setter tablicy elem.whitelist
, możesz to zrobić i zadziała to tak, jak można się spodziewać:
m('my-special-element', {
whitelist: [
'https://example.com',
'https://neverssl.com',
'https://google.com',
],
});
Jeśli masz klasy lub identyfikatory dla tych elementów, skróty nadal działają tak, jak można się spodziewać. Aby przywołać inny przykład A-Frame:
// Te dwa są równoważne
m('a-entity#player');
m('a-entity', { id: 'player' });
Należy pamiętać, że wszystkie właściwości z magiczną semantyką, takie jak atrybuty cyklu życia, obsługi onevent
, key
, class
i style
, są nadal traktowane w taki sam sposób, jak w przypadku normalnych elementów HTML.
Atrybut style
Mithril.js obsługuje zarówno ciągi znaków, jak i obiekty jako prawidłowe wartości style
. Innymi słowy, obsługiwane są wszystkie poniższe:
m('div', { style: 'background:red;' });
m('div', { style: { background: 'red' } });
m('div[style=background:red]');
Użycie ciągu znaków jako style
spowoduje nadpisanie wszystkich stylów wbudowanych w elemencie, jeśli zostanie on ponownie narysowany, a nie tylko reguł CSS, których wartości uległy zmianie.
Można używać nazw właściwości CSS z łącznikami (np. background-color
) oraz nazw właściwości style
DOM pisanych wielbłądem (np. backgroundColor
). Możesz również zdefiniować niestandardowe właściwości CSS, jeśli Twoja przeglądarka je obsługuje.
Mithril.js nie próbuje dodawać jednostek do wartości liczbowych. Po prostu przekształca je w ciągi znaków.
Zdarzenia
Mithril.js obsługuje wiązanie obsługi zdarzeń dla wszystkich zdarzeń DOM, w tym zdarzeń, których specyfikacje nie definiują właściwości on${event}
, takich jak touchstart
.
function doSomething(e) {
console.log(e);
}
m('div', { onclick: doSomething });
Mithril.js akceptuje funkcje i obiekty EventListener. Więc to również zadziała:
var clickListener = {
handleEvent: function (e) {
console.log(e);
},
};
m('div', { onclick: clickListener });
Domyślnie, gdy zdarzenie dołączone za pomocą hyperscript zostanie wywołane, spowoduje to automatyczne ponowne rysowanie przez Mithril.js po powrocie wywołania zwrotnego zdarzenia (zakładając, że używasz m.mount
lub m.route
zamiast bezpośrednio m.render
). Można wyłączyć automatyczne ponowne rysowanie dla konkretnego zdarzenia, ustawiając e.redraw = false
:
m('div', {
onclick: function (e) {
// Zapobiega automatycznemu ponownemu rysowaniu
e.redraw = false;
},
});
Właściwości
Mithril.js obsługuje funkcje DOM, które są dostępne za pośrednictwem właściwości, takich jak właściwości selectedIndex
i value
elementu <select>
.
m('select', { selectedIndex: 0 }, [
m('option', 'Option A'),
m('option', 'Option B'),
]);
Komponenty
Komponenty pozwalają na hermetyzację logiki w jednostkę i używanie jej tak, jakby była elementem. Są one podstawą do tworzenia dużych, skalowalnych aplikacji.
Komponentem jest dowolny obiekt JavaScript zawierający metodę view
. Aby użyć komponentu, przekaż komponent jako pierwszy argument do m()
zamiast przekazywać ciąg selektora CSS. Możesz przekazywać argumenty do komponentu, definiując atrybuty i dzieci, jak pokazano w poniższym przykładzie.
// zdefiniuj komponent
var Greeter = {
view: function (vnode) {
return m('div', vnode.attrs, ['Hello ', vnode.children]);
},
};
// użyj go
m(Greeter, { style: 'color:red;' }, 'world');
// renderuje do następującego HTML:
// <div style="color:red;">Hello world</div>
Aby dowiedzieć się więcej o komponentach, zobacz stronę komponentów.
Metody cyklu życia
Vnode i komponenty mogą mieć metody cyklu życia (znane również jako hooki), które są wywoływane w różnych momentach życia elementu DOM. Metody cyklu życia obsługiwane przez Mithril.js to: oninit
, oncreate
, onupdate
, onbeforeremove
, onremove
i onbeforeupdate
.
Metody cyklu życia są definiowane w taki sam sposób jak obsługi zdarzeń DOM, ale otrzymują vnode jako argument, zamiast obiektu Event:
function initialize(vnode) {
console.log(vnode);
}
m('div', { oninit: initialize });
Hook | Opis |
---|---|
oninit(vnode) | Uruchamia się przed renderowaniem vnode do rzeczywistego elementu DOM. |
oncreate(vnode) | Uruchamia się po dołączeniu vnode do DOM. |
onupdate(vnode) | Uruchamia się za każdym razem, gdy następuje ponowne rysowanie, gdy element DOM jest dołączony do dokumentu. |
onbeforeremove(vnode) | Uruchamia się przed usunięciem elementu DOM z dokumentu. Jeśli zwracana jest obietnica (Promise), Mithril.js odłącza element DOM dopiero po zakończeniu obietnicy. Metoda ta jest wywoływana tylko dla elementu, który jest odłączony od swojego nadrzędnego elementu DOM, ale nie na jego elementach potomnych. |
onremove(vnode) | Uruchamia się przed usunięciem elementu DOM z dokumentu. Jeśli zdefiniowany jest hook onbeforeremove , onremove jest wywoływany po wywołaniu done . Ta metoda jest wyzwalana na elemencie, który jest odłączony od swojego elementu nadrzędnego, i na wszystkich jego elementach potomnych. |
onbeforeupdate(vnode, old) | Uruchamia się przed onupdate i jeśli zwraca false , zapobiega różnicowaniu elementu i wszystkich jego elementów potomnych. |
Aby dowiedzieć się więcej o metodach cyklu życia, zobacz stronę metod cyklu życia.
Key
Elementy vnode na liście mogą posiadać atrybut key
, używany do zarządzania tożsamością elementu DOM przy zmianach danych modelu generujących listę vnode.
Zazwyczaj key
powinien być unikalnym polem identyfikatora obiektów w tablicy danych.
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));
Posiadanie klucza oznacza, że jeśli tablica users
zostanie przetasowana, a widok zostanie ponownie wyrenderowany, pola wejściowe zostaną przetasowane w dokładnie tej samej kolejności, aby zachować prawidłowy fokus i stan DOM.
Aby dowiedzieć się więcej o kluczach, zobacz stronę kluczy.
SVG i MathML
Mithril.js w pełni obsługuje SVG. Xlink jest również obsługiwany, ale w przeciwieństwie do wersji Mithril.js sprzed v1.0, musi mieć jawnie zdefiniowaną przestrzeń nazw:
m('svg', [m("image[xlink:href='image.gif']")]);
MathML jest również w pełni obsługiwany.
Dynamiczne tworzenie szablonów
Ponieważ zagnieżdżone vnode są po prostu zwykłymi wyrażeniami JavaScript, możesz po prostu użyć funkcji JavaScript do manipulowania nimi.
Dynamiczny tekst
var user = { name: 'John' };
m('.name', user.name); // <div class="name">John</div>
Pętle
Użyj metod Array
, takich jak map
, aby iterować po listach danych.
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)
// ))
Warunki
Użyj operatora trójargumentowego, aby warunkowo ustawić zawartość w widoku.
var isError = false;
m('div', isError ? 'An error occurred' : 'Saved'); // <div>Saved</div>
Nie można używać instrukcji JavaScript, takich jak if
lub for
w wyrażeniach JavaScript. Preferowane jest unikanie używania tych instrukcji w ogóle i zamiast tego używanie wyłącznie powyższych konstrukcji, aby zachować liniową i deklaratywną strukturę szablonów.
Konwersja HTML
W Mithril.js poprawnie sformatowany HTML jest prawidłowym JSX. Niewielki wysiłek, poza kopiowaniem i wklejaniem, jest wymagany do zintegrowania niezależnie wyprodukowanego pliku HTML z projektem przy użyciu JSX.
Podczas korzystania z hyperscript konieczne jest przekonwertowanie HTML na składnię hyperscript przed uruchomieniem kodu. Aby to ułatwić, możesz użyć konwertera HTML na szablon Mithril.
Unikaj antywzorców
Chociaż Mithril.js jest elastyczny, niektóre wzorce kodu są odradzane:
Unikaj dynamicznych selektorów
Różne elementy DOM mają różne atrybuty i często różne zachowania. Ustawienie konfigurowalnego selektora może spowodować wyciek szczegółów implementacji komponentu poza jego jednostkę.
// UNIKAJ
var BadInput = {
view: function (vnode) {
return m('div', [m('label'), m(vnode.attrs.type || 'input')]);
},
};
Zamiast dynamicznych selektorów zaleca się wyraźne zaimplementowanie każdej możliwej wersji lub refaktoryzację zmiennej części kodu.
// PREFERUJ jawny kod
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')]);
},
};
// PREFERUJ refaktoryzację zmienności
var BetterLabeledComponent = {
view: function (vnode) {
return m('div', [m('label', vnode.attrs.title), vnode.children]);
},
};
Unikaj tworzenia vnode poza widokami
Jeśli podczas ponownego rysowania napotkany zostanie vnode identyczny z poprzednim, zostanie on pominięty, a jego zawartość nie zostanie zaktualizowana. Chociaż może się to wydawać okazją do optymalizacji wydajności, należy tego unikać, ponieważ blokuje to dynamiczne zmiany w drzewie danego węzła - prowadzi to do efektów ubocznych, takich jak niepowodzenie wyzwalania metod cyklu życia w dół podczas ponownego rysowania. W tym sensie vnode Mithril.js są niezmienne: nowe vnode są porównywane ze starymi; mutacje vnode nie są utrwalane.
Dokumentacja komponentu zawiera więcej szczegółów i przykład tego antywzorca.