Skip to content
Mithril.js 2
Main Navigation РуководствоAPI

Русский

English
简体中文
繁體中文
Español
Français
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Русский

English
简体中文
繁體中文
Español
Français
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Внешний вид

Sidebar Navigation

API

Основной API

m(selector, attributes, children)

render(element, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(object)

buildPathname(object)

parsePathname(string)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

Опциональный API

stream()

Руководство

Содержание страницы

route(root, defaultRoute, routes) ​

Описание ​

Навигация между "страницами" в приложении.

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

m.route(document.body, '/home', {
  '/home': Home, // определяет `https://localhost/#!/home`
});

В приложении может быть только один вызов функции m.route.

Сигнатура ​

m.route(root, defaultRoute, routes)

АргументТипОбязательныйОписание
rootElementДаDOM-элемент, который будет родительским узлом для поддерева.
defaultRouteStringДаМаршрут, на который следует перенаправить, если текущий URL не соответствует ни одному из определенных маршрутов. Обратите внимание, что это не начальный маршрут; начальным маршрутом будет URL в адресной строке.
routesObject<String,Component|RouteResolver>ДаОбъект, где ключи являются строками маршрутов, а значения - компонентами или RouteResolver.
возвращаетundefined

Как читать сигнатуры

Статические члены ​

m.route.set ​

Перенаправляет на указанный маршрут или на маршрут по умолчанию, если соответствующий маршрут не найден. Запускает асинхронную перерисовку всех точек монтирования.

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

АргументТипОбязательныйОписание
pathStringДаИмя пути для маршрутизации, без префикса. Путь может включать параметры, значения которых интерполируются из объекта params.
paramsObjectНетПараметры маршрутизации. Если path содержит слоты для параметров маршрутизации, свойства этого объекта интерполируются в строку пути.
options.replaceBooleanНетУказывает, следует ли создавать новую запись в истории браузера или заменять текущую. По умолчанию false.
options.stateObjectНетОбъект state, который передается в базовый вызов history.pushState / history.replaceState. Этот объект состояния становится доступным в свойстве history.state и добавляется в объект параметров маршрутизации. Обратите внимание, что этот параметр работает только при использовании API pushState, но игнорируется, если маршрутизатор возвращается в режим hashchange (т.е. если API pushState недоступен).
options.titleStringНетСтрока title, которая передается в базовый вызов history.pushState / history.replaceState.
возвращаетundefined

Помните, что при использовании .set с params необходимо также определить соответствующий маршрут:

javascript
var Article = {
  view: function (vnode) {
    return 'This is article ' + vnode.attrs.articleid;
  },
};

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

m.route.get ​

Возвращает последний полностью разрешенный путь маршрутизации без префикса. Он может отличаться от пути, отображаемого в адресной строке, пока асинхронный маршрут находится в ожидании разрешения.

path = m.route.get()

АргументТипОбязательныйОписание
возвращаетStringПоследний полностью разрешенный путь.

m.route.prefix ​

Определяет префикс маршрутизатора. Префикс маршрутизатора - это фрагмент URL-адреса, который определяет базовую стратегию, используемую маршрутизатором.

m.route.prefix = prefix

АргументТипОбязательныйОписание
prefixStringДаПрефикс, который управляет базовой стратегией маршрутизации, используемой Mithril.

Это обычное свойство, поэтому его можно как читать, так и изменять.

m.route.Link ​

Этот компонент создает динамическую ссылку для маршрутизации. Его основная функция - создавать a ссылки с локальными href, преобразованными с учетом префикса маршрута.

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

// Если m.route.prefix не изменился по сравнению со стратегией по умолчанию, отображается:
// <a href="#!/foo">foo</a>

Ссылки принимают набор специальных атрибутов:

  • selector - это то, что будет передано в качестве первого аргумента в m: любой селектор является допустимым, включая элементы, отличные от a.
  • params & options - это аргументы с теми же именами, что и в m.route.set.
  • disabled, если true, отключает поведение маршрутизации и любой связанный обработчик onclick и добавляет атрибут data-disabled="true" для указания состояния недоступности; если элемент является a, атрибут href удаляется.

Поведение маршрутизации нельзя предотвратить с помощью API обработки событий: используйте disabled вместо этого.

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

// Отображается:
// <button disabled aria-disabled="true" class="large">link name</button>

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

АргументТипОбязательныйОписание
attributes.hrefObjectДаЦелевой маршрут для перехода.
attributes.disabledBooleanНетОтключает элемент.
attributes.selectorString|Object|FunctionНетСелектор для m, по умолчанию "a".
attributes.optionsObjectНетУстанавливает options, переданные в m.route.set.
attributes.paramsObjectНетУстанавливает params, переданные в m.route.set.
attributesObjectНетЛюбые другие атрибуты, которые будут переданы в m.
childrenArray<Vnode>|String|Number|BooleanНетДочерние vnodes для этой ссылки.
возвращаетVnodeVnode.

m.route.param ​

Извлекает параметр маршрута из последнего полностью разрешенного маршрута. Параметр маршрута - это пара "ключ-значение". Параметры маршрута могут поступать из нескольких разных источников:

  • Интерполяция маршрута (например, если маршрут /users/:id и он разрешается в /users/1, параметр маршрута имеет ключ id и значение "1").
  • Строка запроса маршрутизатора (например, если путь /users?page=1, параметр маршрута имеет ключ page и значение "1").
  • history.state (например, если history.state - {foo: "bar"}, параметр маршрута имеет ключ foo и значение "bar").

value = m.route.param(key)

АргументТипОбязательныйОписание
keyStringНетИмя параметра маршрута (например, id в маршруте /users/:id или page в пути /users/1?page=3 или ключ в history.state).
возвращаетString|ObjectЗначение для указанного ключа. Если ключ не указан, возвращает объект, содержащий все ключи интерполяции.

Обратите внимание, что в функции onmatch RouteResolver новый маршрут еще не был полностью разрешен, и m.route.param() вернет параметры предыдущего маршрута, если таковые имеются. onmatch получает параметры нового маршрута в качестве аргумента.

m.route.SKIP ​

Специальное значение, которое может быть возвращено из onmatch RouteResolver для перехода к следующему маршруту.

RouteResolver ​

RouteResolver - это объект, не являющийся компонентом, который содержит метод onmatch и/или метод render. Оба метода являются необязательными, но должен присутствовать хотя бы один.

Если объект может быть обнаружен как компонент (по наличию метода view или по тому, что он является function/class), он будет рассматриваться как таковой, даже если у него есть методы onmatch или render. Поскольку RouteResolver не является компонентом, у него нет методов жизненного цикла.

Как правило, RouteResolver должны находиться в том же файле, что и вызов m.route, тогда как определения компонентов должны находиться в своих собственных модулях.

routeResolver = {onmatch, render}

При использовании компонентов вы можете думать о них как о синтаксическом сахаре для этого RouteResolver, предполагая, что ваш компонент - Home:

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

routeResolver.onmatch ​

Хук onmatch вызывается, когда маршрутизатору необходимо найти компонент для рендеринга. Он вызывается один раз для каждого изменения пути маршрутизатора, но не при последующих перерисовках на том же пути. Его можно использовать для запуска логики до инициализации компонента (например, логики аутентификации, предварительной загрузки данных, отслеживания аналитики перенаправления и т. д.).

Этот метод также позволяет определить отображаемый компонент асинхронно, что делает его подходящим для разделения кода и асинхронной загрузки модулей. Чтобы отобразить компонент асинхронно, верните Promise, который разрешается в компонент.

Для получения дополнительной информации об onmatch см. раздел расширенное разрешение компонентов.

routeResolver.onmatch(args, requestedPath, route)

АргументТипОписание
argsObjectПараметры маршрутизации.
requestedPathStringПуть маршрутизатора, запрошенный последним действием маршрутизации, включая интерполированные значения параметров маршрутизации, но без префикса. Когда вызывается onmatch, разрешение для этого пути не завершено, и m.route.get() по-прежнему возвращает предыдущий путь.
routeStringПуть маршрутизатора, запрошенный последним действием маршрутизации, исключая интерполированные значения параметров маршрутизации.
возвращаетComponent|\Promise<Component>|undefinedКомпонент или Promise, который разрешается в компонент.

Если onmatch возвращает компонент или Promise, который разрешается в компонент, этот компонент используется в качестве vnode.tag для первого аргумента в методе render RouteResolver. В противном случае vnode.tag устанавливается в "div". Аналогично, если метод onmatch опущен, vnode.tag также имеет значение "div".

Если onmatch возвращает Promise, который отклоняется, маршрутизатор перенаправляет обратно на defaultRoute. Вы можете переопределить это поведение, вызвав .catch в цепочке Promise перед его возвратом.

routeResolver.render ​

Метод render вызывается при каждой перерисовке для соответствующего маршрута. Он похож на метод view в компонентах и существует для упрощения композиции компонентов. Он также позволяет избежать обычного поведения Mithril.js по замене всего поддерева.

vnode = routeResolver.render(vnode)

АргументТипОписание
vnodeObjectVnode, чей объект атрибутов содержит параметры маршрутизации. Если onmatch не возвращает компонент или Promise, который разрешается в компонент, поле tag vnode по умолчанию имеет значение "div".
vnode.attrsObjectКарта значений параметров URL.
возвращаетArray<Vnode>|VnodeVnodes, которые будут отображены.

Параметр vnode представляет собой m(Component, m.route.param()), где Component - это разрешенный компонент для маршрута (после routeResolver.onmatch), а m.route.param() - как описано здесь. Если вы опустите этот метод, значением возврата по умолчанию будет [vnode], обернутое во фрагмент, чтобы вы могли использовать ключевые параметры. В сочетании с параметром :key он становится одноэлементным фрагментом с ключом, поскольку в конечном итоге он отображается во что-то вроде [m(Component, {key: m.route.param("key"), ...})].

Как это работает ​

Маршрутизация - это система, которая позволяет создавать одностраничные приложения (SPA), то есть приложения, которые могут переходить со "страницы" на другую, не вызывая полной перезагрузки браузера.

Она обеспечивает плавную навигацию, сохраняя при этом возможность добавления каждой страницы в закладки по отдельности и возможность навигации по приложению с помощью механизма истории браузера.

Маршрутизация без перезагрузки страниц частично стала возможной благодаря API history.pushState. Используя этот API, можно программно изменить URL-адрес, отображаемый браузером после загрузки страницы, но разработчик приложения несет ответственность за то, чтобы навигация по любому заданному URL-адресу из "холодного" состояния (например, новой вкладки) отображала соответствующую разметку.

Стратегии маршрутизации ​

Стратегия маршрутизации определяет, как библиотека может фактически реализовать маршрутизацию. Существует три общие стратегии, которые можно использовать для реализации системы маршрутизации SPA, и каждая из них имеет разные особенности:

  • m.route.prefix = '#!' (по умолчанию) - Использование части URL-адреса идентификатора фрагмента (также известного как хеш). URL-адрес, использующий эту стратегию, обычно выглядит как https://localhost/#!/page1.
  • m.route.prefix = '?' - Использование строки запроса. URL-адрес, использующий эту стратегию, обычно выглядит как https://localhost/?/page1.
  • m.route.prefix = '' - Использование имени пути. URL-адрес, использующий эту стратегию, обычно выглядит как https://localhost/page1.

Использование хеш-стратегии гарантированно работает в браузерах, которые не поддерживают history.pushState, поскольку она может вернуться к использованию onhashchange. Используйте эту стратегию, если вы хотите, чтобы хеши были чисто локальными.

Стратегия строки запроса позволяет обнаруживать маршруты на стороне сервера, но URL не отображается как обычный путь. Используйте эту стратегию, если вы хотите поддерживать и потенциально обнаруживать привязанные ссылки на стороне сервера и не можете внести изменения, необходимые для поддержки стратегии имени пути (например, если вы используете Apache и не можете изменить свой .htaccess).

Стратегия имени пути создает самые чистые URL-адреса, но требует настройки сервера для обслуживания кода одностраничного приложения из каждого URL-адреса, на который может маршрутизироваться приложение. Используйте эту стратегию для получения более чистых URL-адресов.

Одностраничные приложения, использующие хеш-стратегию, часто используют соглашение об использовании восклицательного знака после хеша, чтобы указать, что они используют хеш в качестве механизма маршрутизации, а не для целей ссылки на якоря. Строка #! известна как hashbang.

Стратегия по умолчанию использует hashbang.

Типичное использование ​

Обычно вам нужно создать несколько компонентов для сопоставления маршрутов:

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

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

В приведенном выше примере есть два компонента: Home и Page1. Каждый содержит меню и некоторый текст. Само меню определяется как компонент, чтобы избежать повторения:

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

Теперь мы можем определить маршруты и сопоставить наши компоненты с ними:

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

Здесь мы указываем два маршрута: / и /page1, которые отображают соответствующие компоненты, когда пользователь переходит к каждому URL-адресу.

Переход к разным маршрутам ​

В приведенном выше примере компонент Menu имеет два m.route.Link. Это создает элемент, по умолчанию <a>, и настраивает его так, что если пользователь щелкает по нему, он переходит к другому маршруту самостоятельно. Он не переходит удаленно, только локально.

Вы также можете перемещаться программно, через m.route.set(route). Например, m.route.set("/page1").

При переходе между маршрутами префикс маршрутизатора обрабатывается автоматически. Другими словами, опустите hashbang #! (или любой префикс, который вы установили для m.route.prefix), связывая маршруты Mithril.js, в том числе как в m.route.set, так и в m.route.Link.

Обратите внимание, что при переходе между компонентами происходит замена всего поддерева. Используйте route resolver с методом render, если вы хотите просто обновить часть поддерева.

Параметры маршрутизации ​

Иногда мы хотим, чтобы в маршруте отображался переменный id или аналогичные данные, но мы не хотим явно указывать отдельный маршрут для каждого возможного id. Чтобы этого добиться, Mithril.js поддерживает параметризованные маршруты:

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

Это создает динамический маршрут, который соответствует любому URL, начинающемуся с /edit/ и за которым следуют данные. Затем значение id сопоставляется как атрибут vnode компонента (vnode.attrs.id).

В маршруте может быть несколько аргументов, например /edit/:projectID/:userID даст свойства projectID и userID в объекте атрибутов vnode компонента.

Key (ключевой) параметр ​

Когда пользователь переходит с параметризованного маршрута на тот же маршрут с другим параметром (например, переходит с /page/1 на /page/2 при заданном маршруте /page/:id), компонент не будет воссоздан с нуля, поскольку оба маршрута разрешаются в один и тот же компонент, и, таким образом, приведут к виртуальному dom in-place diff. Это имеет побочный эффект запуска хука onupdate, а не oninit/oncreate. Однако разработчику довольно часто требуется синхронизировать воссоздание компонента с событием изменения маршрута.

Чтобы этого добиться, можно объединить параметризацию маршрута с ключами для очень удобного шаблона:

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

Это означает, что vnode, который создается для корневого компонента маршрута, имеет объект параметра маршрута key. Параметры маршрута становятся attrs в vnode. Таким образом, при переходе с одной страницы на другую key изменяется и приводит к воссозданию компонента с нуля (поскольку ключ сообщает движку виртуального dom, что старый и новый компоненты - это разные сущности).

Вы можете развить эту идею дальше, чтобы создавать компоненты, которые воссоздают себя при перезагрузке:

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

Или даже использовать функцию history state для достижения перезагружаемых компонентов, не загрязняя URL-адрес:

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

Обратите внимание, что ключевой параметр работает только для маршрутов компонентов. Если вы используете route resolver, вам нужно будет использовать одноэлементный фрагмент с ключом, передав key: m.route.param("key"), чтобы выполнить то же самое.

Вариативные маршруты ​

Также можно иметь вариативные маршруты, то есть маршрут с аргументом, который содержит имена путей URL-адресов, содержащие косые черты:

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

Обработка 404 ​

Для изоморфного / универсального приложения JavaScript url-параметр и вариативный маршрут в сочетании очень полезны для отображения пользовательской страницы ошибки 404.

В случае ошибки 404 Not Found сервер отправляет обратно пользовательскую страницу клиенту. Когда загружается Mithril.js, он перенаправит клиента на маршрут по умолчанию, потому что он не может знать этот маршрут.

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

History state ​

Можно в полной мере воспользоваться базовым API history.pushState, чтобы улучшить навигацию пользователя. Например, приложение может "запомнить" состояние большой формы, когда пользователь покидает страницу, переходя в другое место, так что если пользователь нажмет кнопку "Назад" в браузере, у него будет заполнена форма, а не пустая форма.

Например, вы можете создать форму следующим образом:

javascript
var state = {
  term: '',
  search: function () {
    // сохраняем состояние для этого маршрута
    // это эквивалентно `history.replaceState({term: state.term}, null, location.href)`
    m.route.set(m.route.get(), null, {
      replace: true,
      state: { term: state.term },
    });

    // перейти
    location.href = 'https://google.com/?q=' + state.term;
  },
};

var Form = {
  oninit: function (vnode) {
    state.term = vnode.attrs.term || ''; // заполняется из свойства `history.state`, если пользователь нажимает кнопку «Назад»
  },
  view: function () {
    return m('form', [
      m("input[placeholder='Search']", {
        oninput: function (e) {
          state.term = e.target.value;
        },
        value: state.term,
      }),
      m('button', { onclick: state.search }, 'Search'),
    ]);
  },
};

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

Таким образом, если пользователь выполнит поиск и нажмет кнопку "Назад", чтобы вернуться в приложение, поле ввода по-прежнему будет заполнено поисковым запросом. Этот метод может улучшить пользовательский опыт в больших формах и других приложениях, где восстановление состояния вручную может быть затруднительным.

Изменение префикса маршрутизатора ​

Префикс маршрутизатора - это фрагмент URL-адреса, который определяет базовую стратегию, используемую маршрутизатором.

javascript
// установить стратегию pathname
m.route.prefix = '';

// установить стратегию querystring
m.route.prefix = '?';

// установить в хеш без восклицательного знака
m.route.prefix = '#';

// установить стратегию pathname на URL, не являющемся корневым
// например, если приложение находится по адресу `https://localhost/my-app`, а что-то другое находится по адресу `https://localhost`
m.route.prefix = '/my-app';

Расширенное разрешение компонентов ​

Вместо непосредственного сопоставления компонента с маршрутом, вы можете использовать объект RouteResolver. Объект RouteResolver содержит методы onmatch() и/или render(). Оба метода необязательны, но необходимо определить хотя бы один из них.

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function (args, requestedPath, route) {
      return Home;
    },
    render: function (vnode) {
      return vnode; // эквивалентно m(Home)
    },
  },
});

RouteResolver'ы полезны для различных продвинутых сценариев маршрутизации.

Оборачивание компонента макета ​

Часто возникает необходимость обернуть все или большинство маршрутизируемых компонентов в переиспользуемый контейнер (обычно называемый "макетом"). Для этого сначала необходимо создать компонент, содержащий общую разметку, которая будет оборачивать другие компоненты:

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

В приведенном выше примере макет состоит только из <div class="layout">, который содержит дочерние элементы, переданные компоненту, но в реальном приложении он может быть гораздо сложнее.

Один из способов обернуть макет - определить анонимный компонент в карте маршрутов:

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

Однако, поскольку компонент верхнего уровня является анонимным, переход с маршрута / на маршрут /form (или наоборот) приведет к удалению анонимного компонента и повторному созданию DOM. Если бы у компонента Layout были определены методы жизненного цикла, хуки oninit и oncreate срабатывали бы при каждом изменении маршрута. В зависимости от приложения, это может быть желательным или нет.

Если вы хотите, чтобы компонент Layout сравнивался на предмет изменений и сохранялся, а не пересоздавался с нуля, используйте RouteResolver в качестве корневого объекта:

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

В этом случае, если у компонента Layout есть методы жизненного цикла oninit и oncreate, они будут срабатывать только при первом изменении маршрута (при условии, что все маршруты используют один и тот же макет).

Для иллюстрации, пример 1 эквивалентен следующему коду:

javascript
// functionally equivalent to example 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);
    },
  },
});

Поскольку Anon1 и Anon2 являются разными компонентами, их поддеревья (включая Layout) воссоздаются с нуля. Это также происходит, когда компоненты используются непосредственно, без RouteResolver.

В примере 2, поскольку Layout является компонентом верхнего уровня в обоих маршрутах, DOM для компонента Layout дифференцируется (то есть остается нетронутым, если в нем нет изменений), и только изменение с Home на Form вызывает воссоздание этого подраздела DOM.

Перенаправление ​

Хук onmatch в RouteResolver можно использовать для выполнения логики до инициализации компонента верхнего уровня в маршруте. Вы можете использовать либо m.route.set() Mithril, либо собственный API history HTML. При использовании API history для перенаправления, onmatch должен возвращать Promise, который никогда не будет выполнен, чтобы предотвратить обработку текущего маршрута. m.route.set() отменяет разрешение соответствующего маршрута, поэтому это не требуется.

Пример: аутентификация ​

В приведенном ниже примере показано, как реализовать страницу авторизации, которая не позволяет пользователям видеть страницу /secret, если они не вошли в систему.

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,
});

При загрузке приложения вызывается onmatch, и поскольку isLoggedIn имеет значение false, приложение перенаправляется на /login. После того, как пользователь нажмет кнопку входа в систему, isLoggedIn будет установлено в true, и приложение будет перенаправлено на /secret. Хук onmatch будет запущен еще раз, и поскольку isLoggedIn на этот раз имеет значение true, приложение отобразит компонент Home.

Для простоты в приведенном выше примере статус входа пользователя в систему хранится в глобальной переменной, и этот флаг просто переключается, когда пользователь нажимает кнопку входа в систему. В реальном приложении пользователь должен будет предоставить надлежащие учетные данные для входа в систему, и нажатие кнопки входа в систему вызовет запрос к серверу для аутентификации пользователя:

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,
});

Предварительная загрузка данных ​

Как правило, компонент может загружать данные при инициализации. Загрузка данных таким образом приводит к двукратной отрисовке компонента. Первый проход отрисовки происходит при маршрутизации, а второй - после того, как запрос завершится. Обратите внимание, что loadUsers() возвращает Promise, но любой Promise, возвращаемый oninit, в настоящее время игнорируется. Второй проход отрисовки происходит из background option for 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';
    },
  },
});

В приведенном выше примере при первой отрисовке пользовательский интерфейс отображает "loading", поскольку state.users является пустым массивом до завершения запроса. Затем, как только данные становятся доступными, интерфейс обновляется и отображается список идентификаторов пользователей.

RouteResolver'ы можно использовать для предварительной загрузки данных перед отрисовкой компонента, чтобы избежать мерцания интерфейса и, как следствие, избежать необходимости в индикаторе загрузки:

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);
      });
    },
  },
});

В этом случае render запускается только после завершения запроса, что делает использование тернарного оператора ненужным.

Разделение кода ​

В большом приложении может быть желательно загружать код для каждого маршрута по требованию, а не заранее. Разделение кодовой базы таким образом известно как разделение кода или отложенная загрузка. В Mithril.js это можно сделать, вернув Promise из хука onmatch:

В своей самой простой форме можно сделать следующее:

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');
    },
  },
});

Однако, на практике, для работы в production-окружении, необходимо объединить все зависимости модуля Home.js в один файл, который будет отдаваться сервером.

К счастью, существует ряд инструментов, которые облегчают задачу объединения модулей для отложенной загрузки. Вот пример использования native dynamic import(...), поддерживаемого многими упаковщиками:

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

Типизированные маршруты ​

В некоторых расширенных случаях маршрутизации может потребоваться ограничить значение параметра не только форматом пути, сопоставляя только что-то вроде числового идентификатора. Вы можете сделать это довольно легко, вернув m.route.SKIP из маршрута.

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

Скрытые маршруты ​

В редких случаях может потребоваться скрыть определенные маршруты для некоторых пользователей, но не для всех. Например, пользователю может быть запрещено просматривать определенного пользователя, и вместо отображения ошибки разрешения вы предпочли бы притвориться, что его не существует, и перенаправить на представление 404. В этом случае вы можете использовать m.route.SKIP, чтобы просто притвориться, что маршрут не существует.

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,
});

Отмена/блокировка маршрута ​

RouteResolver onmatch может предотвратить разрешение маршрута, вернув Promise, который никогда не выполняется. Это можно использовать для обнаружения попыток избыточного разрешения маршрута и их отмены:

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

Интеграция со сторонними библиотеками ​

В некоторых случаях может потребоваться интеграция с другими фреймворками, такими как React. Вот как это сделать:

  • Определите все маршруты, используя m.route как обычно, но убедитесь, что вы используете его только один раз. Использование нескольких точек монтирования маршрутов не поддерживается.
  • Когда вам нужно удалить подписки на маршрутизацию, используйте m.mount(root, null), используя тот же root, который вы использовали m.route(root, ...) на. m.route использует m.mount внутри, чтобы подключить все, так что это не магия.

Вот пример с 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} />;
  }
}

А вот примерный эквивалент с 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
Предыдущая страницаmount(root, component)
Следующая страницаrequest(options)

Выпущено на условиях лицензии MIT.

Авторские права (c) 2024 Mithril Contributors

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

Выпущено на условиях лицензии MIT.

Авторские права (c) 2024 Mithril Contributors