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

Начало работы

Установка Mithril.js

Простое приложение

Ресурсы

JSX

ES6+ в старых браузерах

Анимации

Тестирование

Примеры

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

Обработка путей

Ключевые концепции

Виртуальные DOM-узлы

Компоненты

Методы жизненного цикла

Ключи

Автоматическая перерисовка

Разное

Сравнение фреймворков

Переход с v1.x

Переход с v0.2.x

API

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

Переход с v1.x ​

Версия 2.x практически полностью обратно совместима с v1.x по API, однако существуют некоторые несовместимые изменения.

Присваивание значения vnode.state ​

В v1.x вы могли произвольно манипулировать vnode.state и присваивать ему любые значения. В v2.x попытка изменения vnode.state приведет к ошибке. Способ миграции зависит от конкретного случая, но в большинстве ситуаций достаточно заменить ссылки с vnode.state на vnode.state.foo, выбрав подходящее имя для foo (например, count, если это текущее значение счетчика).

v1.x ​

javascript
var Counter = {
  oninit: function (vnode) {
    vnode.state = 0;
  },
  view: function (vnode) {
    return m('.counter', [
      m(
        'button',
        {
          onclick: function () {
            vnode.state--;
          },
        },
        '-'
      ),
      vnode.state,
      m(
        'button',
        {
          onclick: function () {
            vnode.state++;
          },
        },
        '+'
      ),
    ]);
  },
};

v2.x ​

javascript
var Counter = {
  oninit: function (vnode) {
    vnode.state.count = 0;
  },
  view: function (vnode) {
    return m('.counter', [
      m(
        'button',
        {
          onclick: function () {
            vnode.state.count--;
          },
        },
        '-'
      ),
      vnode.state.count,
      m(
        'button',
        {
          onclick: function () {
            vnode.state.count++;
          },
        },
        '+'
      ),
    ]);
  },
};

Когда v1.0 была впервые выпущена, классовые и замыкающие компоненты еще не существовали, поэтому фреймворк просто использовал то, что ему было нужно, из vnode.tag. Эта деталь реализации позволяла вам это делать, и в некоторых местах документации это подразумевалось как допустимое. Теперь все иначе, и это немного упрощает управление с точки зрения реализации, поскольку есть только одна ссылка на состояние, а не две.

Изменения в якорях маршрутов ​

В v1.x вы использовали oncreate: m.route.link и, если ссылка могла измениться, onupdate: m.route.link. Это были хуки жизненного цикла для vnode, которые можно было маршрутизировать. В v2.x теперь используется компонент m.route.Link. Селектор можно указать через атрибут selector:, если вы использовали что-либо отличное от m("a", ...) Параметры можно указать через options:, вы можете отключить его через disabled:, и другие атрибуты можно указать inline, включая href: (обязательно). Сам selector: может содержать любой селектор, допустимый в качестве первого аргумента для m, а атрибуты [href=...] и [disabled] можно указать как в селекторе, так и в обычных параметрах.

v1.x ​

javascript
m('a', {
  href: '/path',
  oncreate: m.route.link,
});

m('button', {
  href: '/path',
  oncreate: m.route.link,
});

m('button.btn[href=/path]', {
  oncreate: m.route.link,
});

v2.x ​

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

m(m.route.Link, {
  selector: 'button',
  href: '/path',
});

m(m.route.Link, {
  selector: 'button.btn[href=/path]',
});

Изменения в обработке ошибок m.request ​

В v1.x m.request разбирал ошибки из JSON-ответов и присваивал свойства полученного объекта ответа объекту ошибки. Например, если вы получили ответ со статусом 403 и телом {"code": "backoff", "timeout": 1000}, у ошибки были бы два дополнительных свойства: err.code = "backoff" и err.timeout = 1000.

В v2.x ответ присваивается свойству response объекта ошибки, а свойство code содержит полученный код состояния. Таким образом, если вы получили ответ со статусом 403 и телом {"code": "backoff", "timeout": 1000}, ошибка будет иметь два свойства: err.response = {code: "backoff", timeout: 1000} и err.code = 403.

m.withAttr удален ​

В v1.x обработчики событий могли использовать oninput: m.withAttr("value", func) и аналогичные конструкции. В v2.x рекомендуется считывать значения непосредственно из цели события. Это хорошо сочеталось с потоками, но поскольку идиома m.withAttr("value", stream) не была настолько распространена, как m.withAttr("value", prop), m.withAttr потерял большую часть своей полезности и был удален.

v1.x ​

javascript
var value = '';

// In your view
m('input[type=text]', {
  value: value(),
  oninput: m.withAttr('value', function (v) {
    value = v;
  }),
});

// OR

var value = m.stream('');

// In your view
m('input[type=text]', {
  value: value(),
  oninput: m.withAttr('value', value),
});

v2.x ​

javascript
var value = '';

// In your view
m('input[type=text]', {
  value: value,
  oninput: function (ev) {
    value = ev.target.value;
  },
});

// OR

var value = m.stream('');

// In your view
m('input[type=text]', {
  value: value(),
  oninput: function (ev) {
    value(ev.target.value);
  },
});

m.route.prefix ​

В v1.x m.route.prefix была функцией, вызываемой через m.route.prefix(prefix). Теперь это свойство, которое устанавливается с помощью m.route.prefix = prefix.

v1.x ​

javascript
m.route.prefix('/root');

v2.x ​

javascript
m.route.prefix = '/root';

Параметры и тело запросов m.request/m.jsonp ​

data и useBody были реорганизованы в params (параметры запроса, интерполированные в URL и добавленные к запросу) и body (тело для отправки в базовом XHR). Это дает вам гораздо лучший контроль над фактическим отправленным запросом и позволяет как интерполировать параметры запроса с помощью POST-запросов, так и создавать GET-запросы с телами.

m.jsonp, не имеющий значимого "тела", просто использует params, поэтому переименование data в params достаточно для этого метода.

v1.x ​

javascript
m.request('https://example.com/api/user/:id', {
  method: 'GET',
  data: { id: user.id },
});

m.request('https://example.com/api/user/create', {
  method: 'POST',
  data: userData,
});

v2.x ​

javascript
m.request('https://example.com/api/user/:id', {
  method: 'GET',
  params: { id: user.id },
});

m.request('https://example.com/api/user/create', {
  method: 'POST',
  body: userData,
});

Path templates ​

В v1.x существовало три отдельных синтаксиса шаблонов путей, которые, хотя и были похожи, имели 2 отдельно разработанных синтаксиса и 3 разные реализации. Это было определено довольно произвольным образом, и параметры обычно не экранировались. Теперь все либо кодируется, если это :key, либо является неэкранированным, если это :key.... Если что-то неожиданно закодировано, используйте :path.... Это просто.

Конкретно, вот как это влияет на каждый метод:

URL-адреса m.request и m.jsonp, пути m.route.set ​

Компоненты пути в v2.x экранируются автоматически при интерполяции. Предположим, вы вызываете m.route.set("/user/:name/photos/:id", {name: user.name, id: user.id}). Ранее, если user был {name: "a/b", id: "c/d"}, это установило бы маршрут на /user/a%2Fb/photos/c/d, но теперь он установит его на /user/a%2Fb/photos/c%2Fd. Если вы намеренно хотите интерполировать ключ без экранирования, используйте вместо этого :key....

Ключи в v2.x не могут содержать символы . или -. В v1.x они могли содержать все, кроме /.

Интерполяции во встроенных строках запроса, например, в /api/search?q=:query, не выполняются в v2.x. Передавайте их через params с соответствующими именами ключей вместо указания их в строке запроса.

Route patterns m.route ​

Ключи пути в форме :key... возвращали свой URL, декодированный в v1.x, но возвращают raw URL в v2.x.

Ранее такие конструкции, как :key.md, ошибочно принимались, при этом значение результирующего параметра устанавливалось равным keymd: "...". Это больше не так - .md теперь является частью шаблона, а не имени.

Lifecycle call order ​

В v1.x хуки жизненного цикла атрибутов на vnode компонента вызывались до собственных хуков жизненного цикла компонента во всех случаях. В v2.x это относится только к onbeforeupdate. Поэтому вам может потребоваться соответствующим образом скорректировать свой код.

v1.x ​

javascript
var Comp = {
  oncreate: function () {
    console.log('Component oncreate');
  },
  view: function () {
    return m('div');
  },
};

m.mount(document.body, {
  view: function () {
    return m(Comp, {
      oncreate: function () {
        console.log('Attrs oncreate');
      },
    });
  },
});

// Logs:
// Attrs oncreate
// Component oncreate

v2.x ​

javascript
var Comp = {
  oncreate: function () {
    console.log('Component oncreate');
  },
  view: function () {
    return m('div');
  },
};

m.mount(document.body, {
  view: function () {
    return m(Comp, {
      oncreate: function () {
        console.log('Attrs oncreate');
      },
    });
  },
});

// Logs:
// Component oncreate
// Attrs oncreate

Синхронность m.redraw ​

m.redraw() в v2.x всегда асинхронна. Вы можете явно запросить синхронную перерисовку с помощью m.redraw.sync(), если в данный момент не происходит перерисовка.

Selector attribute precedence ​

В v1.x атрибуты селектора имели приоритет над атрибутами, указанными в объекте атрибутов. Например, m("[a=b]", {a: "c"}).attrs возвращал {a: "b"}.

В v2.x атрибуты, указанные в объекте атрибутов, имеют приоритет над атрибутами селектора. Например, m("[a=b]", {a: "c"}).attrs возвращает {a: "c"}.

Обратите внимание, что технически это возврат к поведению v0.2.x.

Children normalization ​

В v1.x дочерние элементы vnode компонента нормализовались, как и другие vnode. В v2.x это больше не так, и вам нужно будет планировать это соответствующим образом. Это не влияет на нормализацию, выполняемую при рендеринге.

m.request headers ​

В v1.x Mithril.js устанавливал эти два заголовка для всех запросов, отличных от GET, но только когда для useBody было установлено значение true (по умолчанию) и выполнялись другие перечисленные условия:

  • Content-Type: application/json; charset=utf-8 для запросов с телами JSON
  • Accept: application/json, text/* для запросов, ожидающих ответы JSON

В v2.x Mithril.js устанавливает первый заголовок для всех запросов с телами JSON, которые != null, и опускает его по умолчанию в противном случае. Это делается независимо от выбранного метода, в том числе и для GET-запросов.

Первый из двух заголовков, Content-Type, вызовет предварительный запрос CORS, поскольку он не является CORS-safelisted request header из-за указанного типа контента, и это может привести к новым ошибкам в зависимости от того, как CORS настроен на вашем сервере. Если у вас возникнут проблемы с этим, вам может потребоваться переопределить этот заголовок, передав headers: {"Content-Type": "text/plain"}. (Заголовок Accept не вызывает проблем, поэтому вам не нужно его переопределять.)

Единственные типы контента, которые спецификация Fetch позволяет избежать проверок предварительной выборки CORS, - это application/x-www-form-urlencoded, multipart/form-data и text/plain. Он не допускает ничего другого и намеренно запрещает JSON.

Query parameters in hash strings in routes ​

В v1.x вы могли указывать параметры запроса для маршрутов как в строке запроса, так и в строке хеша, поэтому m.route.set("/route?foo=1&bar=2"), m.route.set("/route?foo=1#bar=2") и m.route.set("/route#foo=1&bar=2") были эквивалентны, и атрибуты, извлеченные из них, были бы {foo: "1", bar: "2"}.

В v2.x содержимое строк хеша игнорируется, но сохраняется. Итак, атрибуты, извлеченные из каждого из них, будут следующими:

  • m.route.set("/route?foo=1&bar=2") → {foo: "1", bar: "2"}
  • m.route.set("/route?foo=1#bar=2") → {foo: "1"}
  • m.route.set("/route#foo=1&bar=2") → {}

Причина этого в том, что URL-адреса, такие как https://example.com/#!/route#key, технически недействительны согласно спецификации URL и даже были недействительны согласно предшествующему ей RFC, и это всего лишь причуда спецификации HTML, что они разрешены. (Спецификация HTML должна была с самого начала требовать, чтобы идентификаторы и фрагменты местоположения были действительными фрагментами URL, если она хотела следовать спецификации.)

Или, короче говоря, прекратите использовать недействительные URL-адреса!

Keys ​

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

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

Если вам нужно обойти это, используйте идиому фрагмента, содержащего один vnode, например, [m("div", {key: whatever})].

m.version removed ​

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

Pager
Предыдущая страницаСравнение фреймворков
Следующая страницаПереход с v0.2.x

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

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

https://mithril.js.org/migration-v1x.html

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

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