Hyperapp — минималистичный фронтенд-фреймворк для построения веб-приложений на JavaScript, сочетающий декларативный подход к UI и управление состоянием с помощью функциональных концепций. Архитектура Hyperapp основана на нескольких ключевых принципах, которые определяют его дизайн и способ организации кода.
Hyperapp строит интерфейс через декларативное описание дерева элементов. Каждый компонент описывается как функция, возвращающая виртуальное DOM-дерево, которое фреймворк синхронизирует с реальным DOM.
Пример виртуального DOM:
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 }, "Увеличить"),
h("button", { onclick: actions.decrement }, "Уменьшить")
])
);
app({ init: state, view, actions, root: document.body });
Ключевое преимущество такого подхода — явная связь между состоянием и отображением, что исключает необходимость ручной синхронизации DOM.
Hyperapp использует модель однонаправленного потока данных, схожую с Redux:
Такой подход обеспечивает предсказуемость изменений и упрощает отладку, так как любое изменение состояния происходит через определённые точки — actions.
Actions в Hyperapp — это функции, которые получают текущее состояние и возвращают его новую версию. Они могут быть синхронными или асинхронными через эффекты.
Синхронное действие:
const actions = {
increment: () => state => ({ count: state.count + 1 })
};
Асинхронное действие с эффектом:
const actions = {
fetchData: () => (state, actions) => [
state,
fetch("https://api.example.com/data")
.then(res => res.json())
.then(data => actions.setData(data))
],
setData: data => state => ({ data })
};
Использование эффектов отделяет чистую логику изменения состояния от побочных действий, таких как запросы к серверу или таймеры.
Hyperapp поддерживает композицию компонентов, где каждый компонент может содержать собственное состояние и actions, либо использовать глобальное состояние приложения. Компоненты описываются как функции, возвращающие virtual DOM, что облегчает повторное использование и модульное тестирование.
const Counter = ({ count, increment, decrement }) => (
h("div", {}, [
h("h2", {}, count),
h("button", { onclick: increment }, "+"),
h("button", { onclick: decrement }, "-")
])
);
Компоненты можно комбинировать, создавая сложные интерфейсы без потери управляемости состояния.
Hyperapp разделяет чистые действия и
побочные эффекты, позволяя точно контролировать
выполнение асинхронного кода. Эффекты описываются как массив
[state, effectFunction], где effectFunction
получает доступ к действиям и параметрам. Это предотвращает смешивание
логики изменения состояния с внешними операциями.
Пример:
const actions = {
saveData: data => (state, actions) => [
state,
() => fetch("/save", { method: "POST", body: JSON.stringify(data) })
]
};
Такой подход обеспечивает тестируемость и изолированность побочных эффектов.
Hyperapp не навязывает сложные абстракции, предоставляя тонкую обертку над DOM. Это позволяет использовать стандартные события и методы браузера без лишнего кода. Благодаря этому архитектура остаётся простой, а производительность — высокой, так как обновляется только изменившийся виртуальный DOM.
Хотя Hyperapp не включает встроенную маршрутизацию, она легко
интегрируется через сторонние библиотеки или кастомные решения.
Глобальное состояние управляется через единый объект state,
что облегчает синхронизацию данных между различными компонентами и
страницами.
Hyperapp сочетает минимализм с функциональной архитектурой, обеспечивая чистый и управляемый код. Основные принципы — декларативный UI, однонаправленный поток данных и отделение эффектов — позволяют строить масштабируемые и легко поддерживаемые приложения даже при ограниченных ресурсах.