Skip to content
Mithril.js 2
Main Navigation GuíaAPI

Español

English
简体中文
繁體中文
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Español

English
简体中文
繁體中文
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Apariencia

Sidebar Navigation

API

API principal

m(selector, attributes, children)

render(elemento, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(objeto)

buildPathname(objeto)

parsePathname(string)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

API opcional

stream()

Guía

En esta página

route(root, defaultRoute, routes) ​

Descripción ​

Permite la navegación entre "páginas" dentro de una aplicación web.

javascript
var Home = {
  view: function () {
    return 'Bienvenida';
  },
};

m.route(document.body, '/home', {
  '/home': Home, // define `https://localhost/#!/home`
});

Solo se permite una llamada a m.route por aplicación.

Firma ​

m.route(root, defaultRoute, routes)

ArgumentoTipoObligatorioDescripción
rootElementSíUn elemento DOM que será el nodo padre del subárbol renderizado por el enrutador.
defaultRouteStringSíLa ruta a la que se redirigirá si la URL actual no coincide con ninguna de las rutas definidas. Nota: esta no es la ruta inicial que se carga al inicio de la aplicación. La ruta inicial será la URL presente en la barra de direcciones del navegador.
routesObject<String,Component|RouteResolver>SíUn objeto cuyas claves son cadenas de ruta (URLs) y los valores son componentes o un RouteResolver.
devuelveDevuelve undefined

Cómo leer firmas

Miembros estáticos ​

m.route.set ​

Redirige a una ruta coincidente, o a la ruta predeterminada si no se encuentra ninguna. Provoca un redibujo asíncrono en todos los puntos de montaje de Mithril.

m.route.set(path, params, options)

ArgumentoTipoObligatorioDescripción
pathStringSíEl nombre de la ruta a la que se va a dirigir, sin el prefijo del enrutador (por ejemplo, sin #!/). La ruta puede incluir parámetros, que se interpolan con los valores de params.
paramsObjectNoParámetros de enrutamiento. Si path contiene marcadores para parámetros de enrutamiento (por ejemplo, :id), las propiedades de este objeto se interpolan en la cadena de ruta.
options.replaceBooleanNoIndica si se debe crear una nueva entrada en el historial del navegador o reemplazar la actual. El valor predeterminado es false.
options.stateObjectNoEl objeto state para pasar a la llamada subyacente history.pushState / history.replaceState. Este objeto de estado está disponible en la propiedad history.state y se fusiona en el objeto de parámetros de enrutamiento. Ten en cuenta que esta opción solo funciona con la API pushState y se ignora si el enrutador recurre al modo hashchange (es decir, si la API pushState no está disponible).
options.titleStringNoLa cadena title para pasar a la llamada subyacente history.pushState / history.replaceState.
devuelveDevuelve undefined

Recuerda que al usar .set con params también necesitas definir la ruta en la configuración inicial del enrutador:

javascript
var Article = {
  view: function (vnode) {
    return 'Este es el artículo ' + vnode.attrs.articleid;
  },
};

m.route(document.body, '/article/1', {
  '/article/:articleid': Article,
});
m.route.set('/article/:articleid', { articleid: 1 });

m.route.get ​

Devuelve la última ruta completamente resuelta, sin el prefijo del enrutador. Puede diferir de la ruta que se muestra en la barra de ubicación del navegador mientras una ruta asíncrona está pendiente de resolución.

path = m.route.get()

ArgumentoTipoObligatorioDescripción
devuelveStringDevuelve la última ruta completamente resuelta.

m.route.prefix ​

Define el prefijo del enrutador. El prefijo del enrutador es un fragmento de la URL que dicta la estrategia subyacente utilizada por el enrutador.

m.route.prefix = prefix

ArgumentoTipoObligatorioDescripción
prefixStringSíEl prefijo que controla la estrategia de enrutamiento subyacente utilizada por Mithril.

Esta es una propiedad simple, por lo que puedes tanto leerla como escribirla.

m.route.Link ​

Su función principal es crear enlaces <a> dinámicos, modificando los atributos href locales para que coincidan con el prefijo de ruta.

javascript
m(m.route.Link, { href: '/foo' }, 'foo');

// A menos que m.route.prefix haya cambiado de la estrategia predeterminada, se renderiza a:
// <a href="#!/foo">foo</a>

Los enlaces aceptan una selección de atributos especiales:

  • selector es lo que se pasaría como el primer argumento a m: cualquier selector es válido, incluidos los elementos que no son a.
  • params y options son los argumentos con los mismos nombres que se definen en m.route.set.
  • disabled, si es verdadero, deshabilita el comportamiento de enrutamiento y cualquier controlador onclick asociado, y adjunta un atributo data-disabled="true" para sugerencias de accesibilidad; si el elemento es un a, se elimina el href.

Para evitar el comportamiento de enrutamiento, no uses la API de manejo de eventos; usa la propiedad disabled.

javascript
m(
  m.route.Link,
  {
    href: '/foo',
    selector: 'button.large',
    disabled: true,
    params: { key: 'value' },
    options: { replace: true },
  },
  'nombre del enlace'
);

// Se renderiza a:
// <button disabled aria-disabled="true" class="large">nombre del enlace</button>

vnode = m(m.route.Link, attributes, children)

ArgumentoTipoObligatorioDescripción
attributes.hrefObjectSíLa ruta de destino a la que se va a navegar.
attributes.disabledBooleanNoDeshabilita el elemento de forma accesible.
attributes.selectorString|Object|FunctionNoUn selector para m, el valor predeterminado es "a".
attributes.optionsObjectNoEstablece las options pasadas a m.route.set.
attributes.paramsObjectNoEstablece los params pasados a m.route.set.
attributesObjectNoCualquier otro atributo que se reenvíe a m.
childrenArray<Vnode>|String|Number|BooleanNovnodes secundarios para este enlace.
devuelveVnodeUn vnode.

m.route.param ​

Obtiene un parámetro de ruta de la última ruta completamente resuelta. Un parámetro de ruta es un par clave-valor. Los parámetros de ruta pueden provenir de diferentes lugares:

  • interpolaciones de ruta (por ejemplo, si una ruta es /users/:id y se resuelve a /users/1, el parámetro de ruta tiene una clave id y el valor "1")
  • cadenas de consulta del enrutador (por ejemplo, si la ruta es /users?page=1, el parámetro de ruta tiene una clave page y el valor "1")
  • history.state (por ejemplo, si history.state es {foo: "bar"}, el parámetro de ruta tiene la clave foo y el valor "bar")

value = m.route.param(key)

ArgumentoTipoObligatorioDescripción
keyStringNoEl nombre del parámetro de ruta que se desea obtener (por ejemplo, id en la ruta /users/:id, o page en la ruta /users/1?page=3, o una clave en history.state). Si no se especifica una clave, devuelve un objeto que contiene todas las claves de interpolación.
devuelveString|ObjectDevuelve el valor para la clave especificada. Si no se especifica una clave, devuelve un objeto que contiene todos los parámetros de ruta.

Ten en cuenta que en la función onmatch de un RouteResolver, la nueva ruta aún no se ha resuelto por completo y m.route.param() devolverá los parámetros de la ruta anterior, si los hay. onmatch recibe los parámetros de la nueva ruta como un argumento.

m.route.SKIP ​

Un valor especial que se puede devolver desde el onmatch de un resolvedor de rutas para saltar a la siguiente ruta definida en la configuración del enrutador.

RouteResolver ​

Un RouteResolver es un objeto que no es un componente y que contiene un método onmatch y/o un método render. Ambos métodos son opcionales, pero al menos uno debe estar presente.

Si un objeto se puede detectar como un componente (por la presencia de un método view o por ser una function/class), se tratará como tal incluso si tiene métodos onmatch o render. Dado que un RouteResolver no es un componente, no tiene métodos de ciclo de vida.

Como regla general, los RouteResolvers deben estar en el mismo archivo que la llamada m.route, mientras que las definiciones de componentes deben estar en sus propios módulos.

routeResolver = {onmatch, render}

Cuando se utilizan componentes, se puede pensar en ellos como una forma abreviada de este resolvedor de rutas. Por ejemplo, si tu componente es Home:

javascript
var routeResolver = {
  onmatch: function () {
    return Home;
  },
  render: function (vnode) {
    return [vnode];
  },
};

routeResolver.onmatch ​

El hook onmatch se ejecuta cuando el enrutador necesita encontrar un componente para renderizar. Se llama una vez por cada cambio de ruta del enrutador, pero no en los repintados posteriores mientras se está en la misma ruta. Se puede utilizar para ejecutar lógica antes de que se inicialice un componente (por ejemplo, lógica de autenticación, precarga de datos, seguimiento de análisis de redirección, etc.).

Este método también te permite definir de forma asíncrona qué componente se renderizará, lo que lo hace adecuado para la división de código y la carga asíncrona de módulos. Para renderizar un componente de forma asíncrona, devuelve una promesa que se resuelve en un componente.

Para obtener más información sobre onmatch, consulta la sección resolución avanzada de componentes

routeResolver.onmatch(args, requestedPath, route)

ArgumentoTipoDescripción
argsObjectLos parámetros de enrutamiento extraídos de la URL.
requestedPathStringLa ruta solicitada por la última acción de enrutamiento, incluidos los valores de los parámetros de enrutamiento interpolados, pero sin el prefijo del enrutador. Cuando se llama a onmatch, la resolución para esta ruta no está completa y m.route.get() todavía devuelve la ruta anterior.
routeStringLa ruta solicitada por la última acción de enrutamiento, excluyendo los valores de los parámetros de enrutamiento interpolados.
devuelveComponent|\Promise<Component>|undefinedDevuelve un componente, una promesa que se resuelve en un componente, o undefined.

Si onmatch devuelve un componente o una promesa que se resuelve en un componente, este componente se utiliza como el vnode.tag para el primer argumento en el método render del RouteResolver. De lo contrario, el campo tag del vnode se establece por defecto en "div". Del mismo modo, si se omite el método onmatch, vnode.tag también es "div".

Si onmatch devuelve una promesa que se rechaza, el enrutador redirige de nuevo a defaultRoute. Puedes anular este comportamiento llamando a .catch en la cadena de promesas antes de devolverla.

routeResolver.render ​

El método render se llama en cada repintado para una ruta coincidente. Es similar al método view en los componentes y existe para simplificar la composición de componentes. También te permite escapar del comportamiento normal de Mithril.js de reemplazar todo el subárbol.

vnode = routeResolver.render(vnode)

ArgumentoTipoDescripción
vnodeObjectUn vnode cuyo objeto de atributos contiene parámetros de enrutamiento. Si onmatch no devuelve un componente o una promesa que se resuelve en un componente, el campo tag del vnode toma el valor predeterminado "div"
vnode.attrsObjectUn mapa de valores de parámetros de URL
devuelveArray<Vnode>|VnodeLos vnodes que se van a repintar

El parámetro vnode es simplemente m(Component, m.route.param()) donde Component es el componente resuelto para la ruta (después de routeResolver.onmatch) y m.route.param() es como se documenta aquí. Si omites este método, el valor de retorno predeterminado es [vnode], envuelto en un fragmento para que puedas utilizar parámetros de clave. Combinado con un parámetro :key, se convierte en un fragmento con clave de un solo elemento, ya que termina repintando algo como [m(Component, {key: m.route.param("key"), ...})].

Cómo funciona ​

El enrutamiento es un sistema que permite crear aplicaciones de una sola página (SPA), es decir, aplicaciones que pueden ir de una "página" a otra sin provocar una actualización completa del navegador.

Permite una navegabilidad fluida al tiempo que preserva la capacidad de marcar cada página individualmente y la capacidad de navegar por la aplicación a través del mecanismo de historial del navegador.

El enrutamiento sin refrescar la página es posible gracias a la API subyacente history.pushState. El uso de esta API permite cambiar programáticamente la URL que muestra el navegador después de que se haya cargado una página, pero es responsabilidad del desarrollador de la aplicación asegurarse de que la navegación a cualquier URL dada desde un estado frío (por ejemplo, una nueva pestaña) renderice el marcado apropiado.

Estrategias de enrutamiento ​

La estrategia de enrutamiento dicta cómo una biblioteca implementa el enrutamiento. Hay tres estrategias generales que se pueden utilizar para implementar un sistema de enrutamiento SPA, y cada una tiene diferentes consideraciones:

  • m.route.prefix = '#!' (predeterminado) – Utilizando la porción del identificador de fragmento (también conocido como hash) de la URL. Una URL que utiliza esta estrategia normalmente se ve así: https://localhost/#!/page1
  • m.route.prefix = '?' – Utilizando la cadena de consulta. Una URL que utiliza esta estrategia normalmente se ve así: https://localhost/?/page1
  • m.route.prefix = '' – Utilizando el nombre de la ruta. Una URL que utiliza esta estrategia normalmente se ve así: https://localhost/page1

La estrategia hash está garantizada que funcionará en los navegadores que no admiten history.pushState, porque puede recurrir al uso de onhashchange. Utiliza esta estrategia si quieres mantener los hashes puramente locales.

La estrategia de cadena de consulta permite la detección del lado del servidor, pero no aparece como una ruta normal. Utiliza esta estrategia si quieres admitir y potencialmente detectar enlaces anclados del lado del servidor y no puedes realizar los cambios necesarios para admitir la estrategia de nombre de ruta (como si estás utilizando Apache y no puedes modificar tu .htaccess).

La estrategia de nombre de ruta produce las URL de aspecto más limpio, pero requiere configurar el servidor para que sirva el código de la aplicación de una sola página desde cada URL a la que la aplicación puede enrutar. Utiliza esta estrategia si quieres URL de aspecto más limpio.

Las aplicaciones de una sola página que utilizan la estrategia hash a menudo utilizan la convención de tener un signo de exclamación después del hash para indicar que están utilizando el hash como un mecanismo de enrutamiento y no con el propósito de enlazar a anclajes. La cadena #! se denomina hashbang.

La estrategia predeterminada utiliza el hashbang.

Uso típico ​

Normalmente, necesitas crear algunos componentes para asignar rutas a:

javascript
var Home = {
  view: function () {
    return [m(Menu), m('h1', 'Inicio')];
  },
};

var Page1 = {
  view: function () {
    return [m(Menu), m('h1', 'Página 1')];
  },
};

En el ejemplo anterior, hay dos componentes: Home y Page1. Cada uno contiene un menú y algo de texto. El menú en sí se define como un componente para evitar la repetición:

javascript
var Menu = {
  view: function () {
    return m('nav', [
      m(m.route.Link, { href: '/' }, 'Inicio'),
      m(m.route.Link, { href: '/page1' }, 'Página 1'),
    ]);
  },
};

Ahora podemos definir rutas y asignar nuestros componentes a ellas:

javascript
m.route(document.body, '/', {
  '/': Home,
  '/page1': Page1,
});

Aquí especificamos dos rutas: / y /page1, que renderizan sus respectivos componentes cuando el usuario navega a cada URL.

Navegar a diferentes rutas ​

En el ejemplo anterior, el componente Menu tiene dos m.route.Links. Eso crea un elemento, por defecto un <a>, y lo configura para que, si el usuario hace clic en él, navegue a otra ruta de forma local. No navega de forma remota, solo dentro de la aplicación.

También puedes navegar programáticamente, a través de m.route.set(route). Por ejemplo, m.route.set("/page1").

Al navegar entre rutas, el prefijo del enrutador se maneja automáticamente. En otras palabras, omite el hashbang #! (o cualquier prefijo que establezcas en m.route.prefix) al enlazar rutas de Mithril.js, tanto en m.route.set como en m.route.Link.

Ten en cuenta que al navegar entre componentes, se reemplaza todo el subárbol. Utiliza un resolvedor de rutas con un método render si quieres simplemente actualizar el subárbol existente.

Parámetros de enrutamiento ​

A veces queremos que un id variable o información similar aparezcan en una ruta, pero no queremos especificar explícitamente una ruta separada para cada id posible. Para lograr eso, Mithril.js admite rutas parametrizadas:

javascript
var Edit = {
  view: function (vnode) {
    return [m(Menu), m('h1', 'Editando ' + vnode.attrs.id)];
  },
};
m.route(document.body, '/edit/1', {
  '/edit/:id': Edit,
});

En el ejemplo anterior, definimos una ruta /edit/:id. Esto crea una ruta dinámica que coincide con cualquier URL que comience con /edit/ y sea seguida por algunos datos (por ejemplo, /edit/1, edit/234, etc.). El valor id se asigna entonces como un atributo del vnode del componente (vnode.attrs.id)

Es posible tener múltiples argumentos en una ruta, por ejemplo /edit/:projectID/:userID produciría las propiedades projectID y userID en el objeto de atributos del vnode del componente.

Parámetro de clave ​

Cuando un usuario navega de una ruta parametrizada a la misma ruta con un parámetro diferente (por ejemplo, ir de /page/1 a /page/2 dada una ruta /page/:id), el componente no se volvería a crear desde cero ya que ambas rutas se resuelven al mismo componente, y por lo tanto resultan en una actualización en el mismo lugar del DOM virtual. Esto tiene el efecto secundario de desencadenar el hook onupdate, en lugar de oninit/oncreate. Sin embargo, es relativamente común que un desarrollador quiera sincronizar la recreación del componente con el evento de cambio de ruta.

Para lograr eso, es posible combinar la parametrización de la ruta con claves para un patrón muy conveniente:

javascript
m.route(document.body, '/edit/1', {
  '/edit/:key': Edit,
});

Esto significa que el vnode que se crea para el componente raíz de la ruta tiene un objeto de parámetro de ruta clave. Los parámetros de ruta se convierten en attrs en el vnode. Por lo tanto, al saltar de una página a otra, la clave cambia y hace que el componente se vuelva a crear desde cero (ya que la clave le dice al motor DOM virtual que los componentes antiguos y nuevos son entidades diferentes).

Puedes llevar esa idea más allá para crear componentes que se recrean a sí mismos cuando se recargan:

m.route.set(m.route.get(), {key: Date.now()})

O incluso utilizar la función history state para lograr componentes recargables sin contaminar la URL:

m.route.set(m.route.get(), null, {state: {key: Date.now()}})

Ten en cuenta que el parámetro clave solo funciona para rutas de componentes. Si estás utilizando un resolvedor de rutas, tendrás que utilizar un fragmento con clave de un solo hijo, pasando key: m.route.param("key"), para lograr lo mismo.

Rutas variádicas ​

También es posible tener rutas variádicas, es decir, una ruta con un argumento que contiene nombres de ruta de URL que contienen barras diagonales:

javascript
m.route(document.body, '/edit/pictures/image.jpg', {
  '/edit/:file...': Edit,
});

Manejo de 404s ​

Para una aplicación JavaScript isomórfica / universal, un parámetro de URL y una ruta variádica combinados son muy útiles para mostrar una página personalizada de error 404.

En caso de un error 404 No Encontrado, el servidor devuelve una página personalizada al cliente. Sin embargo, cuando Mithril.js se carga, redirige al cliente a la ruta predeterminada porque inicialmente no reconoce la ruta del error.

javascript
m.route(document.body, '/', {
  '/': homeComponent,
  // [...]
  '/:404...': errorPageComponent,
});

History state ​

Es posible aprovechar al máximo la API history.pushState subyacente para mejorar la experiencia de navegación del usuario. Por ejemplo, una aplicación podría "recordar" el estado de un formulario grande cuando el usuario abandona una página navegando fuera de ella, de modo que si el usuario presionara el botón de retroceso en el navegador, tendría el formulario lleno en lugar de un formulario en blanco.

Por ejemplo, podrías crear un formulario como este:

javascript
var state = {
  term: '',
  search: function () {
    // guarda el estado para esta ruta
    // esto es equivalente a `history.replaceState({term: state.term}, null, location.href)`
    m.route.set(m.route.get(), null, {
      replace: true,
      state: { term: state.term },
    });

    // navegar fuera
    location.href = 'https://google.com/?q=' + state.term;
  },
};

var Form = {
  oninit: function (vnode) {
    state.term = vnode.attrs.term||''; // inicializado con el valor de la propiedad `history.state` si el usuario presiona el botón de retroceso
  },
  view: function () {
    return m('form', [
      m("input[placeholder='Buscar']", {
        oninput: function (e) {
          state.term = e.target.value;
        },
        value: state.term,
      }),
      m('button', { onclick: state.search }, 'Buscar'),
    ]);
  },
};

m.route(document.body, '/', {
  '/': Form,
});

De esta manera, si el usuario busca y presiona el botón de retroceso para volver a la aplicación, la entrada todavía estará poblada con el término de búsqueda. Esta técnica puede mejorar la experiencia del usuario de formularios grandes y otras aplicaciones donde el estado no persistente es laborioso para que un usuario lo produzca.

Cambiar el prefijo del enrutador ​

El prefijo del enrutador es un fragmento de la URL que dicta la estrategia subyacente utilizada por el enrutador.

javascript
// establecer la estrategia de nombre de ruta
m.route.prefix = '';

// establecer a la estrategia de cadena de consulta
m.route.prefix = '?';

// establecer a hash sin 'bang' (sin el signo de exclamación)
m.route.prefix = '#';

// establecer la estrategia de nombre de ruta en una URL que no sea la raíz
// por ejemplo, si la aplicación vive bajo `https://localhost/my-app` y algo más
// vive bajo `https://localhost`
m.route.prefix = '/my-app';

Resolución avanzada de componentes ​

En lugar de asignar un componente directamente a una ruta, puedes especificar un objeto RouteResolver. Un objeto RouteResolver contiene un método onmatch() y/o un método render(). Ambos métodos son opcionales, pero al menos uno de ellos debe estar presente.

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function (args, requestedPath, route) {
      return Home;
    },
    render: function (vnode) {
      return vnode; // equivalente a m(Home)
    },
  },
});

Los RouteResolvers son útiles para implementar casos de enrutamiento avanzados.

Envolver un componente de diseño (layout) ​

A menudo es deseable envolver todos o la mayoría de los componentes enrutados en un contenedor reutilizable (a menudo llamado "layout"). Para ello, primero necesitas crear un componente que contenga el marcado común que envolverá los diferentes componentes:

javascript
var Layout = {
  view: function (vnode) {
    return m('.layout', vnode.children);
  },
};

En el ejemplo anterior, el layout simplemente consiste en un <div class="layout"> que contiene los hijos pasados al componente, pero en un escenario real podría ser tan complejo como sea necesario.

Una forma de envolver el layout es definir un componente anónimo directamente en el mapa de rutas:

javascript
// ejemplo 1
m.route(document.body, '/', {
  '/': {
    view: function () {
      return m(Layout, m(Home));
    },
  },
  '/form': {
    view: function () {
      return m(Layout, m(Form));
    },
  },
});

Sin embargo, ten en cuenta que, dado que el componente de nivel superior es anónimo, al navegar de la ruta / a la ruta /form (o viceversa), el componente anónimo se desmontará y el DOM se recreará desde cero. Si el componente Layout tuviera métodos de ciclo de vida definidos, los hooks oninit y oncreate se ejecutarían en cada cambio de ruta. Dependiendo de la aplicación, esto podría ser deseable o no.

Si prefieres que el componente Layout se compare y se mantenga intacto en lugar de recrearse desde cero, debes usar un RouteResolver como componente raíz:

javascript
// ejemplo 2
m.route(document.body, '/', {
  '/': {
    render: function () {
      return m(Layout, m(Home));
    },
  },
  '/form': {
    render: function () {
      return m(Layout, m(Form));
    },
  },
});

Ten en cuenta que en este caso, si el componente Layout tiene métodos de ciclo de vida oninit y oncreate, solo se ejecutarían en el primer cambio de ruta (asumiendo que todas las rutas usan el mismo layout).

Para entender mejor la diferencia entre ambos ejemplos, el ejemplo 1 es funcionalmente equivalente a este código:

javascript
// funcionalmente equivalente al ejemplo 1
var Anon1 = {
  view: function () {
    return m(Layout, m(Home));
  },
};
var Anon2 = {
  view: function () {
    return m(Layout, m(Form));
  },
};

m.route(document.body, '/', {
  '/': {
    render: function () {
      return m(Anon1);
    },
  },
  '/form': {
    render: function () {
      return m(Anon2);
    },
  },
});

Dado que Anon1 y Anon2 son componentes diferentes, sus subárboles (incluido Layout) se recrean desde cero. Esto también es lo que sucede cuando los componentes se usan directamente sin un RouteResolver.

En el ejemplo 2, dado que Layout es el componente de nivel superior en ambas rutas, el DOM para el componente Layout se compara (es decir, se deja intacto si no tiene cambios), y solo el cambio de Home a Form desencadena una recreación de esa subsección del DOM.

Redirección ​

El hook onmatch de RouteResolver se puede usar para ejecutar lógica antes de inicializar el componente de nivel superior en una ruta. Puedes usar m.route.set() de Mithril o la API history nativa de HTML5. Al redirigir con la API history, el hook onmatch debe devolver una promesa que nunca se resuelva para evitar que se complete la ruta coincidente. m.route.set() cancela la resolución de la ruta coincidente internamente, por lo que esto no es necesario con ella.

Ejemplo: autenticación ​

El siguiente ejemplo muestra cómo implementar un muro de inicio de sesión que impide que los usuarios vean la página /secret a menos que inicien sesión.

javascript
var isLoggedIn = false;

var Login = {
  view: function () {
    return m('form', [
      m(
        'button[type=button]',
        {
          onclick: function () {
            isLoggedIn = true;
            m.route.set('/secret');
          },
        },
        'Login'
      ),
    ]);
  },
};

m.route(document.body, '/secret', {
  '/secret': {
    onmatch: function () {
      if (!isLoggedIn) m.route.set('/login');
      else return Home;
    },
  },
  '/login': Login,
});

Cuando se carga la aplicación, se llama a onmatch y, dado que isLoggedIn es falso, la aplicación se redirige a /login. Una vez que el usuario presiona el botón de inicio de sesión, isLoggedIn se establece en verdadero y la aplicación se redirige a /secret. El hook onmatch se ejecutaría una vez más, y dado que isLoggedIn es verdadero esta vez, la aplicación renderizaría el componente Home.

Por simplicidad, en el ejemplo anterior, el estado de inicio de sesión del usuario se mantiene en una variable global, y ese flag simplemente se activa cuando el usuario hace clic en el botón de inicio de sesión. En un escenario real, un usuario obviamente tendría que proporcionar credenciales de inicio de sesión adecuadas, y hacer clic en el botón de inicio de sesión desencadenaría una solicitud a un servidor para autenticar al usuario:

javascript
var Auth = {
  username: '',
  password: '',

  setUsername: function (value) {
    Auth.username = value;
  },
  setPassword: function (value) {
    Auth.password = value;
  },
  login: function () {
    m.request({
      url: '/api/v1/auth',
      params: { username: Auth.username, password: Auth.password },
    }).then(function (data) {
      localStorage.setItem('auth-token', data.token);
      m.route.set('/secret');
    });
  },
};

var Login = {
  view: function () {
    return m('form', [
      m('input[type=text]', {
        oninput: function (e) {
          Auth.setUsername(e.target.value);
        },
        value: Auth.username,
      }),
      m('input[type=password]', {
        oninput: function (e) {
          Auth.setPassword(e.target.value);
        },
        value: Auth.password,
      }),
      m('button[type=button]', { onclick: Auth.login }, 'Login'),
    ]);
  },
};

m.route(document.body, '/secret', {
  '/secret': {
    onmatch: function () {
      if (!localStorage.getItem('auth-token')) m.route.set('/login');
      else return Home;
    },
  },
  '/login': Login,
});

Precarga de datos ​

Normalmente, un componente puede cargar datos al inicializarse. Cargar datos de esta manera hace que el componente se renderice dos veces: la primera renderización ocurre al enrutar, y la segunda, después de que se complete la solicitud. Ten en cuenta que loadUsers() devuelve una Promesa, pero cualquier Promesa devuelta por oninit se ignora actualmente. El segundo pase de renderizado proviene de la opción background para m.request.

javascript
var state = {
  users: [],
  loadUsers: function () {
    return m.request('/api/v1/users').then(function (users) {
      state.users = users;
    });
  },
};

m.route(document.body, '/user/list', {
  '/user/list': {
    oninit: state.loadUsers,
    view: function () {
      return state.users.length > 0
        ? state.users.map(function (user) {
            return m('div', user.id);
          })
        : 'loading';
    },
  },
});

En el ejemplo anterior, en la primera renderización, la UI muestra "loading" ya que state.users es un array vacío antes de que se complete la solicitud. Luego, una vez que los datos están disponibles, la UI se vuelve a dibujar y se muestra una lista de identificadores de usuario.

Los RouteResolvers permiten precargar datos antes de la renderización del componente para evitar el parpadeo de la UI y, por lo tanto, evitar la necesidad de un indicador de carga:

javascript
var state = {
  users: [],
  loadUsers: function () {
    return m.request('/api/v1/users').then(function (users) {
      state.users = users;
    });
  },
};

m.route(document.body, '/user/list', {
  '/user/list': {
    onmatch: state.loadUsers,
    render: function () {
      return state.users.map(function (user) {
        return m('div', user.id);
      });
    },
  },
});

En este caso, render solo se ejecuta después de que se completa la solicitud, lo que hace que el operador ternario sea redundante.

División de código (Code splitting) ​

En una aplicación grande, puede ser deseable descargar el código de cada ruta bajo demanda, en lugar de por adelantado. Esta división del código se denomina 'code splitting' o carga diferida. En Mithril.js, esto se puede lograr devolviendo una promesa desde el hook onmatch:

En su forma más básica, se podría hacer lo siguiente:

javascript
// Home.js
module.export = {
  view: function () {
    return [m(Menu), m('h1', 'Home')];
  },
};
javascript
// index.js
function load(file) {
  return m.request({
    method: 'GET',
    url: file,
    extract: function (xhr) {
      return new Function(
        'var module = {};' + xhr.responseText + ';return module.exports;'
      );
    },
  });
}

m.route(document.body, '/', {
  '/': {
    onmatch: function () {
      return load('Home.js');
    },
  },
});

Sin embargo, para que esto funcione de forma realista en un entorno de producción, sería necesario empaquetar todas las dependencias del módulo Home.js en el archivo que finalmente sirve el servidor.

Afortunadamente, existen varias herramientas que facilitan la tarea de agrupar módulos para la carga diferida. Aquí hay un ejemplo usando import(...) dinámico nativo, compatible con muchos bundlers:

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function () {
      return import('./Home.js');
    },
  },
});

Rutas tipadas ​

En algunos casos avanzados de enrutamiento, es posible que desees restringir un valor más allá del propio path, solo coincidiendo con algo como un ID numérico. Puedes hacer eso con bastante facilidad devolviendo m.route.SKIP desde una ruta.

javascript
m.route(document.body, '/', {
  '/view/:id': {
    onmatch: function (args) {
      if (!/^\d+$/.test(args.id)) return m.route.SKIP;
      return ItemView;
    },
  },
  '/view/:name': UserView,
});

Rutas ocultas ​

En contadas ocasiones, es posible que desees ocultar ciertas rutas para algunos usuarios, pero no para todos. Por ejemplo, a un usuario se le podría prohibir ver a un usuario en particular, y en lugar de mostrar un error de permiso, preferirías fingir que no existe y redirigir a una vista 404 en su lugar. En este caso, puedes usar m.route.SKIP para simular que la ruta no existe.

javascript
m.route(document.body, '/', {
  '/user/:id': {
    onmatch: function (args) {
      return Model.checkViewable(args.id).then(function (viewable) {
        return viewable ? UserView : m.route.SKIP;
      });
    },
  },
  '/:404...': PageNotFound,
});

Cancelación / bloqueo de rutas ​

El onmatch de RouteResolver puede evitar que se resuelva una ruta devolviendo una promesa que nunca se completa. Esto se puede usar para detectar intentos redundantes de resolución de rutas y cancelarlos:

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function (args, requestedPath) {
      if (m.route.get() === requestedPath) return new Promise(function () {});
    },
  },
});

Integración de terceros ​

En algunos casos, podrías necesitar integrar Mithril con otros frameworks como React. Aquí te mostramos cómo hacerlo:

  • Define todas tus rutas usando m.route como de costumbre, pero asegúrate de usarlo solo una vez. No se admiten múltiples puntos de ruta.
  • Cuando necesites eliminar las suscripciones de enrutamiento, usa m.mount(root, null), utilizando el mismo root que usaste en m.route(root, ...) . m.route usa m.mount internamente para conectar todo, por lo que no es magia.

Aquí hay un ejemplo con React:

jsx
class Child extends React.Component {
  constructor(props) {
    super(props);
    this.root = React.createRef();
  }

  componentDidMount() {
    m.route(this.root, '/', {
      // ...
    });
  }

  componentDidUnmount() {
    m.mount(this.root, null);
  }

  render() {
    return <div ref={this.root} />;
  }
}

Y aquí está el equivalente aproximado con Vue:

html
<div ref="root"></div>
javascript
Vue.component('my-child', {
  template: `<div ref="root"></div>`,
  mounted: function () {
    m.route(this.$refs.root, '/', {
      // ...
    });
  },
  destroyed: function () {
    m.mount(this.$refs.root, null);
  },
});
Pager
Anteriormount(root, component)
Siguienterequest(options)

Publicado bajo la licencia MIT.

Copyright (c) 2024 Mithril Contributors

https://mithril.js.org/route.html

Publicado bajo la licencia MIT.

Copyright (c) 2024 Mithril Contributors