m(selector, attributes, children)
Descrição
Representa um elemento HTML em uma view do Mithril.js.
m('div.foo', { style: { color: 'red' } }, 'hello');
// renders to this HTML:
// <div class="foo" style="color: red">hello</div>
Você também pode usar uma sintaxe semelhante a HTML chamada JSX, utilizando Babel para convertê-la em chamadas hyperscript equivalentes. O exemplo abaixo é equivalente ao anterior.
<div class="foo" style="color: red">
hello
</div>
Assinatura
vnode = m(selector, attrs, children)
Argumento | Tipo | Obrigatório | Descrição |
---|---|---|---|
selector | String|Object|Function | Sim | Um seletor CSS ou um componente. |
attrs | Object | Não | Atributos HTML ou propriedades do elemento. |
children | Array<Vnode>|String|Number|Boolean | Não | Vnodes filhos. Pode ser escrito como argumentos splat. |
retorna | Vnode | Um vnode. |
Como funciona
Mithril.js fornece uma função hyperscript m()
, que permite expressar qualquer estrutura HTML usando a sintaxe JavaScript. Ela aceita uma string selector
(obrigatório), um objeto attrs
(opcional) e um array children
(opcional).
m('div', { id: 'box' }, 'hello');
// renders to this HTML:
// <div id="box">hello</div>
A função m()
não retorna um elemento DOM real. Em vez disso, ela retorna um nó DOM virtual, ou vnode, que é um objeto JavaScript que representa o elemento DOM que será criado.
// a vnode
var vnode = {
tag: 'div',
attrs: { id: 'box' },
children: [
/*...*/
],
};
Para transformar um vnode em um elemento DOM real, use a função m.render()
:
m.render(document.body, m('br')); // Insere um <br> em <body>
Chamar m.render()
várias vezes não recria a árvore DOM do zero a cada vez. Em vez disso, cada chamada fará apenas as alterações em uma árvore DOM que forem absolutamente necessárias para refletir a árvore DOM virtual passada para a chamada. Este comportamento é desejável porque recriar o DOM do zero é custoso e causa problemas como perda de foco em campos de entrada, entre outros. Já atualizar apenas o necessário é muito mais rápido e facilita a manutenção de UIs complexas que lidam com várias user stories.
Flexibilidade
A função m()
é polimórfica e aceita um número variável de argumentos. Em outras palavras, é muito flexível no que espera como parâmetros de entrada:
// simple tag
m('div'); // <div></div>
// attributes and children are optional
m('a', { id: 'b' }); // <a id="b"></a>
m('span', 'hello'); // <span>hello</span>
// tag with child nodes
m('ul', [
// <ul>
m('li', 'hello'), // <li>hello</li>
m('li', 'world'), // <li>world</li>
]); // </ul>
// array is optional
m(
'ul', // <ul>
m('li', 'hello'), // <li>hello</li>
m('li', 'world') // <li>world</li>
); // </ul>
Seletores CSS
O primeiro argumento de m()
pode ser qualquer seletor CSS que possa descrever um elemento HTML. Ele aceita qualquer combinação CSS válida de sintaxe #
(id), .
(classe) e []
(atributo).
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 você omitir o nome da tag, Mithril.js assume uma tag div
.
m('.box.box-bordered'); // <div class="box box-bordered"></div>
Normalmente, recomenda-se usar seletores CSS para atributos estáticos (ou seja, atributos cujo valor não muda) e passar um objeto de atributos para valores de atributos dinâmicos.
var currentURL = '/';
m(
'a.link[href=/]',
{
class: currentURL === '/' ? 'selected' : '',
},
'Home'
);
// renders to this HTML:
// <a href="/" class="link selected">Home</a>
Atributos passados como o segundo argumento
Você pode passar atributos, propriedades, eventos e hooks de ciclo de vida no segundo argumento opcional (veja as próximas seções para mais detalhes).
m("button", {
class: "my-button",
onclick: function() {/* ... */},
oncreate: function() {/* ... */}
})
Se o valor de um atributo for null
ou undefined
, ele é tratado como se o atributo estivesse ausente.
Se houver nomes de classe nos argumentos primeiro e segundo de m()
, eles serão mesclados como esperado. Se o valor da classe no segundo argumento for null
ou undefined
, ele será ignorado.
Se outro atributo estiver presente nos argumentos primeiro e segundo, o segundo terá precedência, mesmo que seja null
ou undefined
.
Atributos DOM
Mithril.js utiliza a API JavaScript e a API DOM (setAttribute
) para definir atributos. Isso significa que você pode usar ambas as sintaxes para se referir aos atributos.
Por exemplo, na API JavaScript, o atributo readonly
é chamado de element.readOnly
(observe o maiúsculo). No Mithril.js, todos os seguintes são suportados:
m('input', { readonly: true }); // lowercase
m('input', { readOnly: true }); // uppercase
m('input[readonly]');
m('input[readOnly]');
Isso inclui também elementos personalizados. Por exemplo, você pode usar A-Frame dentro do Mithril.js, sem problemas!
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',
}),
]);
Para elementos personalizados, o Mithril.js não converte automaticamente propriedades para string, caso sejam objetos, números ou outros valores que não sejam strings. Portanto, supondo que você tenha algum elemento personalizado my-special-element
que tenha uma propriedade getter/setter de array elem.whitelist
, você pode fazer isso e funcionaria como esperado:
m('my-special-element', {
whitelist: [
'https://example.com',
'https://neverssl.com',
'https://google.com',
],
});
Se você tiver classes ou IDs para esses elementos, os atalhos ainda funcionarão como esperado. Para usar outro exemplo do A-Frame:
// These two are equivalent
m('a-entity#player');
m('a-entity', { id: 'player' });
Observe que todas as propriedades com semântica mágica, como atributos de ciclo de vida, manipuladores onevent
, key
s, class
e style
, ainda são tratadas da mesma forma que são para elementos HTML normais.
Atributo de estilo
Mithril.js suporta strings e objetos como valores style
válidos. Em outras palavras, todos os seguintes são suportados:
m('div', { style: 'background:red;' });
m('div', { style: { background: 'red' } });
m('div[style=background:red]');
Usar uma string como style
sobrescreverá todos os estilos inline no elemento se ele for redesenhado, e não apenas as regras CSS cujos valores foram alterados.
Você pode usar nomes de propriedade CSS hifenizados (como background-color
) e nomes de propriedade style
DOM em camel case (como backgroundColor
). Você também pode definir propriedades personalizadas CSS, se o seu navegador as suportar.
Mithril.js não tenta adicionar unidades a valores numéricos. Ele simplesmente os converte em string.
Eventos
Mithril.js suporta a vinculação de tratadores de eventos para todos os eventos DOM, incluindo eventos cujas especificações não definem uma propriedade on${event}
, como touchstart
.
function doSomething(e) {
console.log(e);
}
m('div', { onclick: doSomething });
Mithril.js aceita funções e objetos EventListener. Portanto, isso também funcionará:
var clickListener = {
handleEvent: function (e) {
console.log(e);
},
};
m('div', { onclick: clickListener });
Por padrão, quando um evento anexado com hyperscript é disparado, isso acionará o redesenho automático do Mithril.js após o retorno do seu callback de evento (assumindo que você esteja usando m.mount
ou m.route
em vez de m.render
diretamente). Você pode desativar o redesenho automático especificamente para um único evento definindo e.redraw = false
nele:
m('div', {
onclick: function (e) {
// Prevent auto-redraw
e.redraw = false;
},
});
Propriedades
Mithril.js suporta a funcionalidade DOM que é acessível por meio de propriedades, como as propriedades selectedIndex
e value
de <select>
.
m('select', { selectedIndex: 0 }, [
m('option', 'Option A'),
m('option', 'Option B'),
]);
Componentes
Componentes permitem encapsular a lógica em uma unidade e usá-la como se fosse um elemento HTML. Eles são a base para criar aplicações grandes e escaláveis.
Um componente é qualquer objeto JavaScript que contém um método view
. Para consumir um componente, passe o componente como o primeiro argumento para m()
em vez de passar uma string de seletor CSS. Você pode passar argumentos para o componente definindo atributos e filhos, como mostrado no exemplo abaixo.
// define a component
var Greeter = {
view: function (vnode) {
return m('div', vnode.attrs, ['Hello ', vnode.children]);
},
};
// consume it
m(Greeter, { style: 'color:red;' }, 'world');
// renders to this HTML:
// <div style="color:red;">Hello world</div>
Para saber mais sobre componentes, veja a página de componentes.
Métodos de ciclo de vida
Vnodes e componentes podem ter métodos de ciclo de vida (também conhecidos como hooks), que são chamados em vários pontos durante a vida útil de um elemento DOM. Os métodos de ciclo de vida suportados pelo Mithril.js são: oninit
, oncreate
, onupdate
, onbeforeremove
, onremove
e onbeforeupdate
.
Os métodos de ciclo de vida são definidos da mesma forma que os manipuladores de eventos DOM, mas recebem o vnode como um argumento, em vez de um objeto Event:
function initialize(vnode) {
console.log(vnode);
}
m('div', { oninit: initialize });
Hook | Descrição |
---|---|
oninit(vnode) | Executa antes que um vnode seja renderizado em um elemento DOM real. |
oncreate(vnode) | Executa depois que um vnode é anexado ao DOM. |
onupdate(vnode) | Executa cada vez que um redesenho ocorre enquanto o elemento DOM está anexado ao documento. |
onbeforeremove(vnode) | Executa antes que um elemento DOM seja removido do documento. Se uma Promise for retornada, o Mithril.js só desanexará o elemento DOM após a conclusão da Promise. Este método é acionado apenas no elemento que é desanexado de seu elemento DOM pai, mas não em seus elementos filhos. |
onremove(vnode) | Executa antes que um elemento DOM seja removido do documento. Se um hook onbeforeremove for definido, onremove será chamado após a conclusão da Promise. Este método é acionado no elemento que é desanexado de seu elemento pai e em todos os seus filhos. |
onbeforeupdate(vnode, old) | Executa antes de onupdate e, se retornar false , impede um diff para o elemento e todos os seus filhos. |
Para saber mais sobre os métodos de ciclo de vida, veja a página de métodos de ciclo de vida.
Keys
Vnodes em uma lista podem ter um atributo especial chamado key
, que pode ser usado para gerenciar a identidade do elemento DOM conforme os dados do modelo que geram as alterações na lista de vnodes.
Normalmente, key
deve ser o campo identificador exclusivo dos objetos no array de dados.
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));
Ter uma key significa que, se o array users
for embaralhado e a view for renderizada novamente, as entradas serão embaralhadas na mesma ordem exata, de modo a manter o foco correto e o estado DOM.
Para saber mais sobre keys, veja a página de keys.
SVG e MathML
Mithril.js oferece suporte total a SVG. Xlink também é suportado, mas ao contrário das versões pré-v1.0 do Mithril.js, deve ter o namespace definido explicitamente:
m('svg', [m("image[xlink:href='image.gif']")]);
MathML também é totalmente suportado.
Tornando os templates dinâmicos
Como os vnodes aninhados são apenas expressões JavaScript simples, você pode simplesmente usar os recursos do JavaScript para manipulá-los.
Texto dinâmico
var user = { name: 'John' };
m('.name', user.name); // <div class="name">John</div>
Loops
Use métodos Array
como map
para iterar sobre listas de dados.
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)
// ))
Condicionais
Use o operador ternário para definir condicionalmente o conteúdo em uma view.
var isError = false;
m('div', isError ? 'An error occurred' : 'Saved'); // <div>Saved</div>
Você não pode usar instruções JavaScript como if
ou for
dentro de expressões JavaScript. É preferível evitar o uso dessas instruções e, em vez disso, usar as construções acima exclusivamente para manter a estrutura dos templates linear e declarativa.
Convertendo HTML
No Mithril.js, HTML bem formado é JSX válido. Basta copiar e colar, com poucas adaptações, para integrar um arquivo HTML produzido independentemente em um projeto usando JSX.
Ao usar hyperscript, é necessário converter HTML para a sintaxe hyperscript antes que o código possa ser executado. Para facilitar isso, você pode usar o conversor HTML-para-Mithril-template.
Evite Antipadrões
Embora o Mithril.js seja flexível, alguns padrões de código são desaconselhados:
Evite seletores dinâmicos
Diferentes elementos DOM têm diferentes atributos e, muitas vezes, diferentes comportamentos. Tornar um seletor configurável pode expor os detalhes de implementação de um componente para fora de sua unidade.
// AVOID
var BadInput = {
view: function (vnode) {
return m('div', [m('label'), m(vnode.attrs.type || 'input')]);
},
};
Em vez de tornar os seletores dinâmicos, você é encorajado a codificar explicitamente cada possibilidade válida ou refatorar a parte variável do código.
// PREFER explicit code
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')]);
},
};
// PREFER refactor variability out
var BetterLabeledComponent = {
view: function (vnode) {
return m('div', [m('label', vnode.attrs.title), vnode.children]);
},
};
Evite criar vnodes fora das views
Quando um redesenho encontra um vnode que é estritamente igual ao do render anterior, ele será ignorado e seu conteúdo não será atualizado. Embora isso possa parecer uma oportunidade para otimização de desempenho, deve ser evitado porque impede mudanças dinâmicas na árvore desse nó - isso leva a efeitos colaterais, como falha na ativação de métodos de ciclo de vida downstream no redesenho. Nesse sentido, os vnodes do Mithril.js são imutáveis: novos vnodes são comparados aos antigos; as mutações nos vnodes não são mantidas.
A documentação do componente contém mais detalhes e um exemplo deste anti-pattern.