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

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

JSX ​

Описание ​

JSX — это расширение синтаксиса JavaScript, позволяющее встраивать HTML-теги непосредственно в код JavaScript. Он не является частью стандарта JavaScript и не обязателен для разработки приложений, но может быть предпочтительным синтаксисом для вас или вашей команды.

jsx
function MyComponent() {
  return {
    view: () => m('main', [m('h1', 'Hello world')]),
  };
}

// можно записать как:

function MyComponent() {
  return {
    view: () => (
      <main>
        <h1>Hello world</h1>
      </main>
    ),
  };
}

При использовании JSX вы можете вставлять JavaScript-выражения внутрь JSX-тегов, используя фигурные скобки:

jsx
var greeting = 'Hello';
var url = 'https://google.com';
var link = <a href={url}>{greeting}!</a>;
// выводит <a href="https://google.com">Hello!</a>

Компоненты можно использовать, указывая имя компонента с заглавной буквы или обращаясь к нему как к свойству:

jsx
m.render(document.body, <MyComponent />)
// эквивалентно m.render(document.body, m(MyComponent))
<m.route.Link href="/home">Go home</m.route.Link>
// эквивалентно m(m.route.Link, {href: "/home"}, "Go home")

Настройка ​

Рекомендуемый способ использования JSX — с помощью плагина Babel.

Babel требует npm, который автоматически устанавливается при установке Node.js. После установки npm создайте папку проекта и выполните следующую команду:

bash
npm init -y

Если вы хотите использовать Webpack и Babel вместе, перейдите к разделу ниже.

Чтобы установить Babel как отдельный инструмент, используйте эту команду:

bash
npm install @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-react-jsx --save-dev

Создайте файл .babelrc:

json
{
  "presets": ["@babel/preset-env"],
  "plugins": [
    [
      "@babel/plugin-transform-react-jsx",
      {
        "pragma": "m",
        "pragmaFrag": "'['"
      }
    ]
  ]
}

Чтобы запустить Babel, настройте npm-скрипт. Откройте package.json и добавьте следующую запись в раздел "scripts":

json
{
  "name": "my-project",
  "scripts": {
    "babel": "babel src --out-dir bin --source-maps"
  }
}

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

bash
npm run babel

Использование Babel с Webpack ​

Если вы еще не установили Webpack в качестве сборщика модулей, используйте эту команду:

bash
npm install webpack webpack-cli --save-dev

Вы можете интегрировать Babel с Webpack, выполнив следующие шаги.

bash
npm install @babel/core babel-loader @babel/preset-env @babel/plugin-transform-react-jsx --save-dev

Создайте файл .babelrc:

json
{
  "presets": ["@babel/preset-env"],
  "plugins": [
    [
      "@babel/plugin-transform-react-jsx",
      {
        "pragma": "m",
        "pragmaFrag": "'['"
      }
    ]
  ]
}

Затем создайте файл с именем webpack.config.js

jsx
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, './bin'),
    filename: 'app.js',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /\/node_modules\//,
        use: {
          loader: 'babel-loader',
        },
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};

Если вы знакомы с Webpack, учтите, что добавление параметров Babel непосредственно в раздел babel-loader файла webpack.config.js приведет к ошибке. Вместо этого укажите их в отдельном файле .babelrc.

Эта конфигурация предполагает, что файл исходного кода для точки входа приложения находится в src/index.js, и он выведет бандл в bin/app.js.

Чтобы запустить сборщик модулей, настройте npm-скрипт. Откройте package.json и добавьте следующую запись в раздел "scripts":

json
{
  "name": "my-project",
  "scripts": {
    "start": "webpack --mode development --watch"
  }
}

Теперь вы можете запустить сборщик модулей, выполнив следующую команду в командной строке:

bash
npm start

Продакшен сборка ​

Чтобы сгенерировать минифицированный файл, откройте package.json и добавьте новый npm-скрипт с именем build:

json
{
  "name": "my-project",
  "scripts": {
    "start": "webpack -d --watch",
    "build": "webpack -p"
  }
}

Вы можете использовать хуки в своей рабочей среде, чтобы автоматически запускать скрипт сборки для production. Вот пример для Heroku:

json
{
  "name": "my-project",
  "scripts": {
    "start": "webpack -d --watch",
    "build": "webpack -p",
    "heroku-postbuild": "webpack -p"
  }
}

Предоставление m глобально доступным ​

Чтобы получить доступ к m глобально из всего вашего проекта, сначала импортируйте webpack в ваш webpack.config.js следующим образом:

js
const webpack = require('webpack');

Затем создайте новый плагин в свойстве plugins объекта конфигурации Webpack:

js
{
  plugins: [
    new webpack.ProvidePlugin({
      m: 'mithril',
    }),
  ];
}

Смотрите документацию Webpack для получения дополнительной информации о ProvidePlugin.

Отличия от React ​

JSX в Mithril отличается от JSX в React некоторыми важными нюансами.

Соглашения о регистре для атрибутов и свойств style ​

React требует использования camelCase для имен DOM-свойств вместо HTML-атрибутов, за исключением data-* и aria-*. Например, использование className вместо class и htmlFor вместо for. В Mithril более принято использовать имена HTML-атрибутов в нижнем регистре. Mithril всегда пытается установить атрибуты, если свойство не существует, что более интуитивно соответствует HTML. Обратите внимание, что в большинстве случаев имена свойств DOM и HTML-атрибутов либо одинаковы, либо очень похожи. Например, value/checked для inputs и глобальный атрибут tabindex против свойства elem.tabIndex для HTML-элементов. Очень редко они отличаются чем-то большим, чем регистр: свойство elem.className для атрибута class или свойство elem.htmlFor для атрибута for — одни из немногих исключений.

Аналогично, React всегда использует имена свойств стиля в camelCase, представленные в DOM через свойства elem.style (например, cssHeight и backgroundColor). Mithril поддерживает как это, так и имена CSS-свойств в стиле kebab-case (например, height и background-color) и обычно предпочитает последнее. Только cssHeight, cssFloat и свойства с префиксами поставщиков отличаются чем-то большим, чем регистр.

DOM events ​

React использует camelCase для именования обработчиков событий: onClick для события click, onSubmit для события submit. Некоторые дополнительно изменяются, поскольку они представляют собой несколько слов, объединенных вместе. Например, onMouseMove прослушивает события mousemove. Mithril не выполняет это преобразование регистра, а просто добавляет on к имени нативного события, поэтому вы добавите прослушиватели для onclick и onmousemove, чтобы прослушивать эти два события соответственно. Это гораздо больше соответствует схеме именования HTML и гораздо более интуитивно понятно, если вы пришли из HTML или vanilla DOM.

React поддерживает планирование прослушивателей событий во время фазы захвата (capture phase) (в первом проходе, снаружи внутрь, в отличие от фазы всплытия (bubble phase) по умолчанию, идущей внутрь наружу во втором проходе), добавляя Capture к этому событию. Mithril в настоящее время не имеет такой функциональности, но она может появиться в будущем. Если это необходимо, вы можете вручную добавлять и удалять свои собственные прослушиватели в lifecycle hooks.

JSX vs hyperscript ​

JSX и hyperscript — это два разных синтаксиса для описания vnodes, каждый из которых имеет свои особенности:

  • JSX гораздо более понятен, если вы пришли из HTML/XML и вам удобнее описывать DOM-элементы с помощью такого синтаксиса. Он также немного чище во многих случаях, поскольку использует меньше знаков препинания, а атрибуты содержат меньше визуального шума, поэтому многим людям его гораздо легче читать. И, конечно, многие распространенные редакторы предоставляют поддержку автозаполнения для DOM-элементов так же, как и для HTML. Однако для его использования требуется дополнительный этап сборки, поддержка редакторов не так широка, как в обычном JS, и он значительно более многословен. Он также немного более многословен при работе с большим количеством динамического контента, потому что вам нужно использовать интерполяции для всего.

  • Hyperscript может быть более предпочтительным, если у вас больше опыта в серверном JavaScript, чем в HTML/XML. Он более лаконичен с меньшим количеством избыточности и предоставляет удобный CSS-подобный синтаксис для статических классов, идентификаторов и других атрибутов. Его также можно использовать без этапа сборки, хотя вы можете добавить его, если хотите. И с ним немного легче работать при большом количестве динамического контента, потому что вам не нужно ничего "интерполировать". Однако краткость затрудняет чтение для некоторых людей, особенно для тех, у кого меньше опыта и кто пришел из HTML/CSS/XML, и я не знаю ни одного плагина, который автоматически завершает части селекторов hyperscript, такие как идентификаторы, классы и атрибуты.

Вы можете увидеть, как особенности вступают в игру в более сложных деревьях. Например, рассмотрим это дерево hyperscript, адаптированное из реального проекта @dead-claudia с некоторыми изменениями для ясности и удобочитаемости:

javascript
function SummaryView() {
  let tag, posts;

  function init({ attrs }) {
    Model.sendView(attrs.tag != null);
    if (attrs.tag != null) {
      tag = attrs.tag.toLowerCase();
      posts = Model.getTag(tag);
    } else {
      tag = undefined;
      posts = Model.posts;
    }
  }

  function feed(type, href) {
    return m('.feed', [
      type,
      m('a', { href }, m('img.feed-icon[src=./feed-icon-16.gif]')),
    ]);
  }

  return {
    oninit: init,
    // Чтобы тег правильно сравнивался при изменении маршрута.
    onbeforeupdate: init,
    view: () =>
      m('.blog-summary', [
        m('p', 'My ramblings about everything'),

        m('.feeds', [
          feed('Atom', 'blog.atom.xml'),
          feed('RSS', 'blog.rss.xml'),
        ]),

        tag != null
          ? m(TagHeader, { len: posts.length, tag })
          : m('.summary-header', [
              m('.summary-title', 'Posts, sorted by most recent.'),
              m(TagSearch),
            ]),

        m(
          '.blog-list',
          posts.map(post =>
            m(
              m.route.Link,
              {
                class: 'blog-entry',
                href: `/posts/${post.url}`,
              },
              [
                m(
                  '.post-date',
                  post.date.toLocaleDateString('en-US', {
                    year: 'numeric',
                    month: 'long',
                    day: 'numeric',
                  })
                ),

                m('.post-stub', [
                  m('.post-title', post.title),
                  m('.post-preview', post.preview, '...'),
                ]),

                m(TagList, { post, tag }),
              ]
            )
          )
        ),
      ]),
  };
}

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

jsx
function SummaryView() {
  let tag, posts;

  function init({ attrs }) {
    Model.sendView(attrs.tag != null);
    if (attrs.tag != null) {
      tag = attrs.tag.toLowerCase();
      posts = Model.getTag(tag);
    } else {
      tag = undefined;
      posts = Model.posts;
    }
  }

  function feed(type, href) {
    return (
      <div class="feed">
        {type}
        <a href={href}>
          <img class="feed-icon" src="./feed-icon-16.gif" />
        </a>
      </div>
    );
  }

  return {
    oninit: init,
    // Чтобы тег правильно дифференцировался при смене маршрута.
    onbeforeupdate: init,
    view: () => (
      <div class="blog-summary">
        <p>My ramblings about everything</p>

        <div class="feeds">
          {feed('Atom', 'blog.atom.xml')}
          {feed('RSS', 'blog.rss.xml')}
        </div>

        {tag != null ? (
          <TagHeader len={posts.length} tag={tag} />
        ) : (
          <div class="summary-header">
            <div class="summary-title">Posts, sorted by most recent</div>
            <TagSearch />
          </div>
        )}

        <div class="blog-list">
          {posts.map(post => (
            <m.route.Link class="blog-entry" href={`/posts/${post.url}`}>
              <div class="post-date">
                {post.date.toLocaleDateString('en-US', {
                  year: 'numeric',
                  month: 'long',
                  day: 'numeric',
                })}
              </div>

              <div class="post-stub">
                <div class="post-title">{post.title}</div>
                <div class="post-preview">{post.preview}...</div>
              </div>

              <TagList post={post} tag={tag} />
            </m.route.Link>
          ))}
        </div>
      </div>
    ),
  };
}

Советы и хитрости ​

Преобразование HTML в JSX ​

В Mithril.js валидный HTML обычно является валидным JSX. Для того чтобы все просто работало, требуется немного больше, чем просто вставка необработанного HTML. Единственное, что вам обычно нужно сделать, это изменить значения свойств без кавычек, такие как attr=value, на attr="value", и изменить самозакрывающиеся теги, такие как <input>, на <input />. Это связано с тем, что JSX основан на XML, а не на HTML.

При использовании hyperscript вам часто необходимо преобразовать HTML в синтаксис hyperscript, чтобы использовать его. Чтобы ускорить этот процесс, вы можете использовать созданный сообществом конвертер HTML в Mithril-шаблон, чтобы сделать большую часть работы за вас.

Pager
Предыдущая страницаПростое приложение
Следующая страницаES6+ в старых браузерах

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

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

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

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

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