Функциональные компоненты

Функциональные компоненты в Mithril представляют собой чистые функции, возвращающие виртуальный DOM. Они не имеют собственного состояния и методов жизненного цикла, что делает их лёгкими и предсказуемыми. Основная идея заключается в том, что компонент — это функция () => m(), где m() — это создание виртуального DOM-узла.

const Hello = () => m("h1", "Привет, Mithril!");

Вызов m(Hello) в m.mount или внутри другого компонента отрендерит элемент <h1>Привет, Mithril!</h1>.

Синтаксис и структура

Функциональный компонент может принимать объект attrs, содержащий свойства компонента. Эти атрибуты позволяют передавать данные внутрь компонента, аналогично пропсам в React.

const Greeting = ({ name }) => m("h2", `Привет, ${name}!`);

m.mount(document.body, {
    view: () => m(Greeting, { name: "Мир" })
});

Ключевые моменты:

  • Все атрибуты доступны через параметр функции.
  • Внутри компонента можно использовать любые JavaScript-выражения.
  • Компонент всегда возвращает результат вызова m().

Вложенные функциональные компоненты

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

const Button = ({ label, onclick }) => m("button", { onclick }, label);
const Toolbar = () => m("div.toolbar", [
    m(Button, { label: "Сохранить", onclick: () => console.log("Сохранено") }),
    m(Button, { label: "Отмена", onclick: () => console.log("Отменено") })
]);

m.mount(document.body, Toolbar);
  • Массивы элементов используются для группировки дочерних компонентов.
  • Атрибуты передаются автоматически при использовании m(Component, attrs).

Обработка событий

Функциональные компоненты полностью поддерживают события. Атрибуты onclick, oninput, onchange и другие можно передавать как обычные функции.

const Counter = ({ count, onIncrement }) => 
    m("div", [
        m("span", `Счётчик: ${count}`),
        m("button", { onclick: onIncrement }, "Увеличить")
    ]);

let counter = 0;

m.mount(document.body, {
    view: () => m(Counter, { 
        count: counter, 
        onIncrement: () => { counter++; m.redraw(); }
    })
});

Особенности Mithril:

  • Перерисовка компонентов происходит автоматически при изменении данных через m.redraw().
  • События можно комбинировать с внешними функциями, состояниями и атрибутами.

Использование контекста и состояния

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

const Timer = () => {
    let seconds = 0;
    setInterval(() => { seconds++; m.redraw(); }, 1000);

    return () => m("div", `Прошло секунд: ${seconds}`);
};

m.mount(document.body, Timer);
  • Переменные внутри функции сохраняются благодаря замыканиям.
  • Функция возвращает другую функцию view, которая вызывается для рендера.
  • Внешние состояния могут управляться через объекты или глобальные хранилища.

Комбинация с JSX

Если проект настроен на использование JSX, функциональные компоненты Mithril можно писать в виде привычного JSX:

/** @jsx m */
const Message = ({ text }) => <p>{text}</p>;

m.mount(document.body, {
    view: () => <Message text="Привет с JSX" />
});
  • Необходима настройка Babel для поддержки JSX с Mithril.
  • m() остаётся внутренней функцией JSX-транспиляции.

Оптимизация функциональных компонентов

Функциональные компоненты в Mithril очень лёгкие и быстрые. Для оптимизации можно использовать:

  • Минимизацию перерисовки: избегать создания новых объектов атрибутов без необходимости.
  • Использование ключей (key): помогает Mithril корректно обновлять список элементов.
const List = ({ items }) => 
    m("ul", items.map(item => m("li", { key: item.id }, item.name)));
  • Мемоизация сложных вычислений: вынести тяжелые операции вне функции view.

Взаимодействие с обычными компонентами

Функциональные компоненты полностью совместимы с объектными компонентами Mithril, что позволяет постепенно заменять классы на функции:

const ObjectComponent = {
    view: ({ attrs }) => m("div", `Привет, ${attrs.name}`)
};

const App = () => m("div", [
    m(ObjectComponent, { name: "Мир" }),
    m(Greeting, { name: "Mithril" })
]);

m.mount(document.body, App);
  • Функциональные и объектные компоненты могут использоваться вместе.
  • Атрибуты передаются одинаково, что упрощает интеграцию.

Вывод

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