Hyperapp — минималистичный фронтенд-фреймворк для построения интерфейсов на JavaScript, сочетающий декларативный подход с функциональной реактивностью. Центральными концепциями являются state, actions и view. State хранит текущее состояние приложения, actions изменяют состояние, а view описывает структуру интерфейса на основе состояния.
Пример базового приложения:
import { h, app } from "hyperapp";
const state = { count: 0 };
const actions = {
increment: value => state => ({ count: state.count + value }),
decrement: value => state => ({ count: state.count - value })
};
const view = (state, actions) => (
h("div", {}, [
h("h1", {}, state.count),
h("button", { onclick: () => actions.increment(1) }, "Increment"),
h("button", { onclick: () => actions.decrement(1) }, "Decrement")
])
);
app({ init: state, view, actions, node: document.getElementById("app") });
Ключевые моменты:
Hyperapp поощряет иммутабельное состояние, что упрощает предсказуемость приложения и упрощает рефакторинг. Каждое действие не мутирует состояние напрямую, а возвращает его копию с изменениями.
Пример с вложенным состоянием:
const state = {
user: { name: "Alice", age: 25 },
loggedIn: false
};
const actions = {
setName: name => state => ({ user: { ...state.user, name } }),
toggleLogin: () => state => ({ loggedIn: !state.loggedIn })
};
Использование спред-оператора ... позволяет создавать
новые объекты без изменения исходного состояния.
Hyperapp поддерживает разбиение интерфейса на модули и подкомпоненты, которые принимают state и actions как аргументы. Это облегчает рефакторинг и повторное использование кода.
Пример компонента:
const Counter = ({ count, increment, decrement }) =>
h("div", {}, [
h("h2", {}, count),
h("button", { onclick: () => increment(1) }, "+"),
h("button", { onclick: () => decrement(1) }, "-")
]);
const view = (state, actions) =>
h("div", {}, [
h("h1", {}, "Counters"),
Counter({ count: state.count, increment: actions.increment, decrement: actions.decrement })
]);
Ключевые моменты:
Hyperapp позволяет управлять побочными эффектами через effects. Эффекты описываются как функции, которые получают аргументы и state, а затем могут вызывать внешние API, таймеры, локальное хранилище и т. д.
Пример эффекта для HTTP-запроса:
const fetchData = url => (state, actions) => {
fetch(url)
.then(res => res.json())
.then(data => actions.setData(data));
};
const actions = {
setData: data => () => ({ data }),
loadData: url => fetchData(url)
};
Эффекты позволяют вынести асинхронную логику из компонентов и actions, что делает код более чистым и тестируемым.
При рефакторинге существующего Hyperapp-кода важно учитывать следующие принципы:
components, actions,
effects, state улучшает читаемость.Пример преобразования монолитного action:
До:
const actions = {
updateUser: (name, age) => state => {
state.user.name = name;
state.user.age = age;
localStorage.setItem("user", JSON.stringify(state.user));
return state;
}
};
После:
const actions = {
setUser: user => () => ({ user }),
persistUser: user => () => {
localStorage.setItem("user", JSON.stringify(user));
},
updateUser: (name, age) => (state, actions) => {
const user = { ...state.user, name, age };
actions.setUser(user);
actions.persistUser(user);
}
};
Такой подход обеспечивает чистоту действий, упрощает тестирование и облегчает поддержку.
Hyperapp уже очень легковесен, но при работе с крупными приложениями важно следить за:
h("li", { key: item.id }, item.name).Hyperapp может сочетаться с современными инструментами:
Пример тестирования action с Jest:
test("increment increases count", () => {
const state = { count: 1 };
const newState = actions.increment(2)(state);
expect(newState.count).toBe(3);
});
Hyperapp сочетает минимализм с мощной функциональной архитектурой. Его подход к иммутабельности, модульности и чистым функциям позволяет писать поддерживаемый код и упрощает рефакторинг даже сложных легаси-проектов.