Миграция с других фреймворков на Hyperapp

Hyperapp — минималистичный фронтенд-фреймворк для создания одностраничных приложений (SPA) на JavaScript. Он опирается на унифицированную модель управления состоянием, виртуальный DOM и функциональный подход к обновлению интерфейса. Основное отличие Hyperapp от более тяжеловесных фреймворков, таких как React или Vue, заключается в крайне компактной архитектуре: весь фреймворк умещается в несколько килобайт, при этом сохраняя поддержку модульности и реактивности.

Приложение в Hyperapp строится вокруг трёх ключевых элементов:

  • state — объект, хранящий состояние приложения;
  • actions — функции, изменяющие состояние;
  • view — функция, описывающая интерфейс на основе состояния.

Пример базовой структуры:

import { h, app } from "hyperapp";

const state = { count: 0 };

const actions = {
  increment: () => state => ({ count: state.count + 1 }),
  decrement: () => state => ({ count: state.count - 1 })
};

const view = (state, actions) => (
  h("div", {}, [
    h("h1", {}, state.count),
    h("button", { onclick: actions.increment }, "Increment"),
    h("button", { onclick: actions.decrement }, "Decrement")
  ])
);

app({ init: state, view, actions, node: document.getElementById("app") });

Преобразование компонентов

В Hyperapp отсутствует концепция классовых компонентов, как в React. Компоненты строятся функционально, принимая состояние и действия, возвращая виртуальный DOM:

const Counter = ({ count, increment, decrement }) => 
  h("div", {}, [
    h("h1", {}, count),
    h("button", { onclick: increment }, "Increment"),
    h("button", { onclick: decrement }, "Decrement")
  ]);

При миграции с Vue или React ключевым моментом становится переход от компонентов с внутренним состоянием к централизованному state. Любое локальное состояние теперь должно быть вынесено в глобальный объект state, а взаимодействие осуществляется через actions.

Управление состоянием

Hyperapp использует иммутабельное состояние и функции для его изменения. Каждое действие возвращает новый объект состояния:

const actions = {
  toggle: () => state => ({ visible: !state.visible })
};

При миграции важно учитывать, что mutating state напрямую, как это часто делается в Vue через this.someValue = ..., недопустимо. Все изменения должны проходить через actions.

События и связывание данных

События в Hyperapp обрабатываются через объект с именем события. Например:

h("button", { onclick: actions.increment }, "Increment")

При миграции с jQuery или Angular, где события часто назначаются через селекторы или директивы, нужно переписать логику на чистые функции действий, что обеспечивает предсказуемость и легкую отладку.

Работа с эффектами

Hyperapp поддерживает эффекты (effects) для работы с побочными действиями, такими как HTTP-запросы или работа с localStorage:

const fetchData = () => (state, actions) => [
  state,
  (dispatch, props) => fetch("/api/data")
    .then(res => res.json())
    .then(data => dispatch(actions.setData, data))
];

Миграция с Redux или Vuex может потребовать переписывания middleware или thunk’ов в эффекты Hyperapp. Главный принцип — эффекты отделяются от чистого состояния.

Работа с роутингом

Hyperapp не содержит встроенного роутера, но для SPA используется сторонний пакет @hyperapp/router. Пример маршрутизации:

import { h, app } from "hyperapp";
import { location, Route, Link, Switch } from "@hyperapp/router";

const state = {};
const actions = {};

const Home = () => h("h1", {}, "Home");
const About = () => h("h1", {}, "About");

const view = (state, actions) =>
  h("div", {}, [
    h(Link, { to: "/" }, "Home"),
    h(Link, { to: "/about" }, "About"),
    h(Switch, {}, [
      h(Route, { path: "/", render: Home }),
      h(Route, { path: "/about", render: About })
    ])
  ]);

app({ init: state, view, actions, node: document.getElementById("app"), modules: [location] });

При переходе с Vue Router или React Router нужно переписать маршруты в функциональном стиле, где каждый компонент отображается через render.

Работа с формами и вводом данных

Hyperapp поддерживает двустороннюю привязку данных через actions:

const state = { text: "" };
const actions = {
  setText: value => () => ({ text: value })
};

const view = (state, actions) =>
  h("input", { value: state.text, oninput: e => actions.setText(e.target.value) });

Миграция с Angular Forms или React Controlled Components упрощается: все данные проходят через state и обновляются через чистые функции.

Организация больших приложений

Для крупных проектов рекомендуется использовать модульное разделение состояния и действий:

// modules/counter.js
export const state = { count: 0 };
export const actions = {
  increment: () => state => ({ count: state.count + 1 })
};

// modules/todo.js
export const state = { items: [] };
export const actions = {
  addItem: item => state => ({ items: [...state.items, item] })
};

// main.js
import { app } from "hyperapp";
import * as counter from "./modules/counter";
import * as todo from "./modules/todo";

const state = { ...counter.state, ...todo.state };
const actions = { ...counter.actions, ...todo.actions };

app({ init: state, view, actions, node: document.getElementById("app") });

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

Выводы по миграции

При переходе на Hyperapp ключевыми принципами становятся:

  • Централизация состояния — локальное состояние компонентов сводится к глобальному state.
  • Функциональный подход к UI — все компоненты и действия описываются как чистые функции.
  • Иммутабельность — любое изменение состояния создаёт новый объект, исключая прямые мутации.
  • Эффекты для побочных действий — запросы, таймеры и другие side-effects отделяются от состояния.
  • Простая маршрутизация и формы — функциональный подход к маршрутам и контролируемым элементам формы упрощает перенос логики из других фреймворков.

Эти принципы обеспечивают лёгкость, компактность и предсказуемость приложений на Hyperapp, что делает его привлекательным для проектов, где важна минимальная инфраструктура и высокая производительность.