Поэтапная модернизация legacy кода

Mithril — это лёгкий и быстрый JavaScript-фреймворк для построения одностраничных приложений. Основой работы является виртуальный DOM, который минимизирует количество операций с реальным DOM, повышая производительность. Главные компоненты Mithril:

  • m() — функция для создания виртуальных DOM-элементов.
  • m.route — маршрутизация приложения.
  • m.request — работа с HTTP-запросами.
  • Компоненты — объекты или функции, возвращающие виртуальные элементы.

Простейший пример компонента:

const Hello = {
    view: () => m("h1", "Привет, Mithril!")
};
m.mount(document.body, Hello);

Здесь view возвращает виртуальный DOM-элемент, а m.mount монтирует компонент в реальный DOM.

Подготовка legacy-кода к модернизации

Legacy-код часто построен без компонентного подхода. Перед интеграцией Mithril важно:

  1. Изолировать UI-блоки. Каждую интерактивную часть выделить в отдельный контейнер.
  2. Определить точки входа данных. Любой глобальный state лучше обернуть в отдельные объекты или модели.
  3. Создать слои абстракции для сторонних библиотек. Mithril будет управлять DOM, поэтому прямое вмешательство из legacy-скриптов нужно минимизировать.

Постепенное внедрение компонентов

Внедрение Mithril начинается с небольших UI-блоков:

  1. Выделить конкретный видимый элемент на странице.
  2. Создать для него Mithril-компонент.
  3. Подключить через m.mount или m.render без полного переписывания всей страницы.

Пример:

// Legacy-кнопка
<button id="like-btn">Like</button>

// Mithril-компонент
const LikeButton = {
    count: 0,
    view: () => m("button", {
        onclick: () => LikeButton.count++,
    }, `Like (${LikeButton.count})`)
};

// Монтирование компонента поверх существующего блока
m.mount(document.getElementById("like-btn-container"), LikeButton);

Такой подход позволяет внедрять Mithril по частям, минимизируя риск поломки существующей логики.

Работа с данными и моделями

Mithril не навязывает конкретную структуру данных, но рекомендует использовать объекты и методы для управления состоянием:

const TodoModel = {
    list: [],
    add(todo) {
        this.list.push(todo);
    },
    remove(index) {
        this.list.splice(index, 1);
    }
};

Для связи с сервером используется m.request:

m.request({
    method: "GET",
    url: "/api/todos"
}).then(data => {
    TodoModel.list = data;
});

Особенность: Mithril автоматически обновляет DOM после завершения асинхронного запроса, если состояние было изменено внутри компонента.

Роутинг и ленивое подключение модулей

m.route обеспечивает управление состоянием URL и компонентами:

m.route(document.body, "/home", {
    "/home": HomeComponent,
    "/about": AboutComponent
});

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

const AboutComponent = () => import('./about.js').then(mod => mod.default);

Интеграция с существующими событиями

Legacy-код часто использует прямые DOM-обработчики. Mithril позволяет подключать события через атрибуты:

m("input", {
    oninput: (e) => TodoModel.newItem = e.target.value,
    value: TodoModel.newItem || ""
})

Чтобы избежать конфликтов, желательно:

  • Удалить старые inline-события.
  • Синхронизировать состояние через модели.
  • Использовать onbeforeremove и oncreate для управления сторонними плагинами.

Переход к полностью компонентной архитектуре

После постепенного внедрения Mithril-компонентов наступает этап реорганизации:

  1. Перевод каждого UI-блока в компонент. Legacy-функции заменяются методами компонентов.
  2. Централизация состояния. Общие данные выносятся в модели.
  3. Упрощение роутинга. Каждое представление страницы становится отдельным компонентом.
  4. Удаление прямых манипуляций DOM. Весь рендер и обновление состояния осуществляется через m().

Полезные паттерны

  • Композиция компонентов: один компонент использует другой, передавая параметры через attrs.
  • Контейнеры и презентационные компоненты: контейнер управляет состоянием, презентационный только отображает.
  • Асинхронные view: использование промисов внутри view с последующим обновлением через m.redraw.

Поддержка и тестирование

Для безопасной модернизации:

  • Покрывать новые компоненты юнит-тестами.
  • Использовать m.redraw.strategy("diff") для контроля обновлений.
  • Постепенно отключать legacy-скрипты, проверяя работу каждого блока.

Этот подход позволяет перевести крупный проект на Mithril без полной переписи кода, сохранив стабильность и минимизируя риски.