Hyperapp — минималистичный фронтенд-фреймворк для создания одностраничных приложений (SPA) на JavaScript. Он опирается на унифицированную модель управления состоянием, виртуальный DOM и функциональный подход к обновлению интерфейса. Основное отличие Hyperapp от более тяжеловесных фреймворков, таких как React или Vue, заключается в крайне компактной архитектуре: весь фреймворк умещается в несколько килобайт, при этом сохраняя поддержку модульности и реактивности.
Приложение в Hyperapp строится вокруг трёх ключевых элементов:
Пример базовой структуры:
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 ключевыми принципами становятся:
Эти принципы обеспечивают лёгкость, компактность и предсказуемость приложений на Hyperapp, что делает его привлекательным для проектов, где важна минимальная инфраструктура и высокая производительность.