m(selector, attributes, children)
Descripción
Representa un elemento HTML en la vista de Mithril.js.
m('div.foo', { style: { color: 'red' } }, 'hello');
// se renderiza como este HTML:
// <div class="foo" style="color: red">hello</div>
También puedes usar una sintaxis similar a HTML llamada JSX, usando Babel para convertirla a llamadas hyperscript equivalentes. Esto es equivalente a lo que se mostró anteriormente.
<div class="foo" style="color: red">
hello
</div>
Firma
vnode = m(selector, attrs, children)
Argumento | Tipo | Requerido | Descripción |
---|---|---|---|
selector | String|Object|Function | Sí | Un selector CSS o un componente. |
attrs | Object | No | Atributos HTML o propiedades del elemento. |
children | Array<Vnode>|String|Number|Boolean | No | Vnodes hijos. Se pueden escribir como argumentos splat. |
devuelve | Vnode | Un vnode. |
Cómo funciona
Mithril.js proporciona una función hyperscript m()
, que permite expresar cualquier estructura HTML usando sintaxis JavaScript. Acepta una cadena selector
(obligatoria), un objeto attrs
(opcional) y un array children
(opcional).
m('div', { id: 'box' }, 'hello');
// se renderiza a este HTML:
// <div id="box">hello</div>
La función m()
no devuelve un elemento DOM real. En su lugar, devuelve un nodo DOM virtual, o vnode, un objeto JavaScript que representa el elemento DOM que se creará.
// un vnode
var vnode = {
tag: 'div',
attrs: { id: 'box' },
children: [
/*...*/
],
};
Para transformar un vnode en un elemento DOM real, usa la función m.render()
:
m.render(document.body, m('br')); // inserta un <br> en <body>
Llamar a m.render()
varias veces no recrea el árbol DOM desde cero cada vez. En cambio, cada llamada modificará el árbol DOM solo si es estrictamente necesario para reflejar el árbol DOM virtual proporcionado. Este comportamiento es deseable porque recrear el DOM desde cero es muy costoso y puede causar problemas, como la pérdida del foco en campos de entrada. Por el contrario, actualizar el DOM solo donde sea necesario es comparativamente mucho más rápido y facilita el mantenimiento de interfaces de usuario complejas que manejan múltiples interacciones del usuario.
Flexibilidad
La función m()
es tanto polimórfica como variádica, lo que significa que es muy flexible en cuanto a los parámetros de entrada que acepta:
// etiqueta simple
m('div'); // <div></div>
// los atributos y los hijos son opcionales
m('a', { id: 'b' }); // <a id="b"></a>
m('span', 'hello'); // <span>hello</span>
// etiqueta con nodos hijos
m('ul', [
// <ul>
m('li', 'hello'), // <li>hello</li>
m('li', 'world'), // <li>world</li>
]); // </ul>
// el array es opcional
m(
'ul', // <ul>
m('li', 'hello'), // <li>hello</li>
m('li', 'world') // <li>world</li>
); // </ul>
Selectores CSS
El primer argumento de m()
puede ser cualquier selector CSS que pueda describir un elemento HTML. Acepta cualquier combinación CSS válida de sintaxis #
(id), .
(clase) y []
(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>
Si se omite el nombre de la etiqueta, Mithril.js asume que es una etiqueta div
.
m('.box.box-bordered'); // <div class="box box-bordered"></div>
Normalmente, se recomienda que uses selectores CSS para atributos estáticos (es decir, atributos cuyo valor no cambia) y pases un objeto de atributos para valores de atributos dinámicos.
var currentURL = '/';
m(
'a.link[href=/]',
{
class: currentURL === '/' ? 'selected' : '',
},
'Home'
);
// se renderiza a este HTML:
// <a href="/" class="link selected">Home</a>
Atributos pasados como el segundo argumento
En el segundo argumento (opcional) se pueden pasar atributos, propiedades, eventos y hooks de ciclo de vida (ver las siguientes secciones para más detalles).
m("button", {
class: "my-button",
onclick: function() {/* ... */},
oncreate: function() {/* ... */}
})
Si el valor de un atributo es null
o undefined
, se trata como si el atributo estuviera ausente.
Si hay nombres de clase tanto en el primer como en el segundo argumento de m()
, se fusionan como cabría esperar. Si el valor de la clase en el segundo argumento es null
o undefined
, se ignora.
Si otro atributo está presente tanto en el primer como en el segundo argumento, el segundo tiene prioridad incluso si es null
o undefined
.
Atributos DOM
Mithril.js usa tanto la API de JavaScript como la API DOM (setAttribute
) para establecer atributos. Esto significa que puedes usar ambas sintaxis para referirte a los atributos.
Por ejemplo, en la API de JavaScript, el atributo readonly
se llama element.readOnly
(fíjate en la mayúscula). En Mithril.js, se admiten todas las siguientes:
m('input', { readonly: true }); // minúscula
m('input', { readOnly: true }); // mayúscula
m('input[readonly]');
m('input[readOnly]');
Esto incluye incluso elementos personalizados. Por ejemplo, puedes usar A-Frame dentro de Mithril.js, ¡sin problema!
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, no convierte automáticamente las propiedades en cadenas, en caso de que sean objetos, números o algún otro valor que no sea una cadena. Entonces, asumiendo que tienes algún elemento personalizado my-special-element
que tiene una propiedad getter/setter de array elem.whitelist
, podrías hacer esto, y funcionaría como esperarías:
m('my-special-element', {
whitelist: [
'https://example.com',
'https://neverssl.com',
'https://google.com',
],
});
Si tienes clases o IDs para esos elementos, las abreviaturas siguen funcionando como esperarías. Para extraer otro ejemplo de A-Frame:
// Estos dos son equivalentes
m('a-entity#player');
m('a-entity', { id: 'player' });
Ten en cuenta que todas las propiedades con semántica especial, como los atributos de ciclo de vida, los controladores onevent
, las key
s, class
y style
, se tratan de la misma manera que para los elementos HTML normales.
Atributo de estilo
Mithril.js admite tanto cadenas como objetos como valores style
válidos. En otras palabras, se admiten todas las siguientes:
m('div', { style: 'background:red;' });
m('div', { style: { background: 'red' } });
m('div[style=background:red]');
Usar una cadena como valor para style
sobrescribirá todos los estilos en línea del elemento al redibujarse, no solo las reglas CSS cuyos valores hayan cambiado.
Puedes usar tanto nombres de propiedad CSS con guiones (como background-color
) como nombres de propiedad style
DOM con camelCase (como backgroundColor
). También puedes definir propiedades personalizadas de CSS, si tu navegador las admite.
Mithril.js no intenta agregar unidades a los valores numéricos. Simplemente los convierte en cadenas.
Eventos
Mithril.js admite el enlace de controladores de eventos para todos los eventos DOM, incluidos los eventos cuyas especificaciones no definen una propiedad on${event}
, como touchstart
.
function doSomething(e) {
console.log(e);
}
m('div', { onclick: doSomething });
Mithril.js acepta funciones y objetos EventListener. Así que esto también funcionará:
var clickListener = {
handleEvent: function (e) {
console.log(e);
},
};
m('div', { onclick: clickListener });
Por defecto, cuando se dispara un evento asociado mediante hyperscript, Mithril.js activará un redibujo automático tras la finalización de la función de callback (siempre que se esté usando m.mount
o m.route
en lugar de m.render
directamente). Puedes deshabilitar el redibujo automático específicamente para un solo evento configurando e.redraw = false
en él:
m('div', {
onclick: function (e) {
// Prevenir el redibujo automático
e.redraw = false;
},
});
Propiedades
Mithril.js admite la funcionalidad DOM que es accesible a través de propiedades como las propiedades selectedIndex
y value
del elemento <select>
.
m('select', { selectedIndex: 0 }, [
m('option', 'Option A'),
m('option', 'Option B'),
]);
Componentes
Los Componentes permiten encapsular la lógica en una unidad y usarlos como si fueran elementos. Son la base para crear aplicaciones grandes y escalables.
Un componente es cualquier objeto JavaScript que contiene un método view
. Para utilizar un componente, pasa el componente como el primer argumento a m()
en lugar de pasar una cadena de selector CSS. Puedes pasar argumentos al componente definiendo atributos e hijos, como se muestra en el siguiente ejemplo.
// define un componente
var Greeter = {
view: function (vnode) {
return m('div', vnode.attrs, ['Hello ', vnode.children]);
},
};
// úsalo
m(Greeter, { style: 'color:red;' }, 'world');
// se renderiza a este HTML:
// <div style="color:red;">Hello world</div>
Para obtener más información sobre los componentes, consulta la página de componentes.
Métodos de ciclo de vida
Los vnodes y los componentes pueden tener métodos de ciclo de vida (también conocidos como ganchos), que se llaman en varios puntos durante la vida útil de un elemento DOM. Los métodos de ciclo de vida admitidos por Mithril.js son: oninit
, oncreate
, onupdate
, onbeforeremove
, onremove
y onbeforeupdate
.
Los métodos de ciclo de vida se definen de la misma manera que los controladores de eventos DOM, pero reciben el vnode como un argumento, en lugar de un objeto Event:
function initialize(vnode) {
console.log(vnode);
}
m('div', { oninit: initialize });
Gancho | Descripción |
---|---|
oninit(vnode) | Se ejecuta antes de que un vnode se renderice como un elemento DOM real. |
oncreate(vnode) | Se ejecuta después de que un vnode se agrega al DOM. |
onupdate(vnode) | Se ejecuta cada vez que se produce un redibujo mientras el elemento DOM está adjunto al documento. |
onbeforeremove(vnode) | Se ejecuta antes de que se elimine un elemento DOM del documento. Si se devuelve una Promesa, Mithril.js solo separará el elemento DOM una vez que la promesa se haya completado. |
onremove(vnode) | Se ejecuta antes de que se elimine un elemento DOM del documento. Si se define un gancho onbeforeremove , onremove se llama después de que se llama a done . Este método se activa en el elemento que se separa de su elemento principal y en todos sus hijos. |
onbeforeupdate(vnode, old) | Se ejecuta antes de onupdate y, si devuelve false , evita una comparación (diff) para el elemento y todos sus hijos. |
Para obtener más información sobre los métodos de ciclo de vida, consulta la página de métodos de ciclo de vida.
Keys
Los vnodes en una lista pueden tener un atributo especial llamado key
, que se puede usar para administrar la identidad del elemento DOM a medida que cambian los datos del modelo que generan la lista de vnodes.
Normalmente, key
debe ser el campo identificador único de los objetos en el array de datos.
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));
El uso de una key garantiza que, si el array users
se reordena y la vista se vuelve a renderizar, las entradas se reordenarán de la misma manera, manteniendo el foco y el estado DOM correctos.
Para obtener más información sobre las keys, consulta la página de keys.
SVG y MathML
Mithril.js es totalmente compatible con SVG. Xlink también es compatible, pero a diferencia de las versiones anteriores a la v1.0 de Mithril.js, debe tener el espacio de nombres definido explícitamente:
m('svg', [m("image[xlink:href='image.gif']")]);
MathML también es totalmente compatible.
Hacer que las plantillas sean dinámicas
Dado que los vnodes anidados son simplemente expresiones de JavaScript, se pueden manipular directamente usando las características del lenguaje.
Texto dinámico
var user = { name: 'John' };
m('.name', user.name); // <div class="name">John</div>
Bucles
Usa métodos de Array
como map
para iterar sobre listas de datos.
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)
// ))
Condicionales
Usa el operador ternario para establecer condicionalmente el contenido en una vista.
var isError = false;
m('div', isError ? 'An error occurred' : 'Saved'); // <div>Saved</div>
No puedes usar sentencias de JavaScript como if
o for
dentro de expresiones de JavaScript. Es preferible evitar el uso de esas sentencias por completo y, en cambio, usar las construcciones anteriores exclusivamente para mantener la estructura de las plantillas lineal y declarativa.
Convertir HTML
En Mithril.js, el HTML bien formado es JSX válido. Basta con copiar y pegar para integrar un archivo HTML producido independientemente en un proyecto usando JSX.
Cuando se usa hyperscript, es necesario convertir HTML a sintaxis hyperscript antes de que se pueda ejecutar el código. Para facilitar esto, puedes usar el convertidor de HTML a plantilla de Mithril.
Evitar antipatrones
Aunque Mithril.js es flexible, algunos patrones de código están desaconsejados:
Evitar selectores dinámicos
Diferentes elementos DOM tienen diferentes atributos y, a menudo, diferentes comportamientos. Hacer que un selector sea configurable puede filtrar los detalles de implementación de un componente fuera de su unidad.
// EVITAR
var BadInput = {
view: function (vnode) {
return m('div', [m('label'), m(vnode.attrs.type||'input')]);
},
};
En lugar de usar selectores dinámicos, se recomienda codificar explícitamente cada posibilidad o refactorizar la parte variable del código.
// PREFERIR código explícito
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')]);
},
};
// PREFERIR refactorizar la variabilidad
var BetterLabeledComponent = {
view: function (vnode) {
return m('div', [m('label', vnode.attrs.title), vnode.children]);
},
};
Evitar crear vnodes fuera de las vistas
Si un redibujo encuentra un vnode idéntico al del renderizado anterior, se omitirá y su contenido no se actualizará. Si bien esto puede parecer una oportunidad para la optimización del rendimiento, debe evitarse porque impide los cambios dinámicos en el árbol de ese nodo; esto conduce a efectos secundarios, como el hecho de que los métodos de ciclo de vida descendentes no se activen en el redibujo. En este sentido, los vnodes de Mithril.js son inmutables: los vnodes nuevos se comparan con los antiguos; las mutaciones a los vnodes no se conservan.
La documentación del componente contiene más detalles y un ejemplo de este antipatrón.