Hyperapp представляет собой минималистичный JavaScript-фреймворк для построения веб-приложений с декларативным управлением состоянием и виртуальным DOM. Его компактность и простая архитектура позволяют строить приложения с высокой модульностью, разделяя код на независимые и повторно используемые части.
В Hyperapp приложение обычно состоит из трёх ключевых компонентов:
Модульная организация предполагает раздельное хранение этих компонентов, что улучшает читаемость и тестируемость кода.
Пример структуры проекта:
src/
├── app.js
├── state/
│ ├── user.js
│ └── tasks.js
├── actions/
│ ├── userActions.js
│ └── taskActions.js
├── views/
│ ├── userView.js
│ └── taskView.js
└── components/
├── Button.js
└── Modal.js
Каждый модуль отвечает за конкретный функциональный блок приложения. Это позволяет легко расширять проект и изолировать изменения.
Состояние и действия в Hyperapp могут быть сгруппированы по функциональным модулям. Например, модуль управления пользователями может содержать:
state/user.js
export const state = {
currentUser: null,
usersList: []
};
actions/userActions.js
export const actions = {
setUser: (user) => () => ({ currentUser: user }),
addUser: (user) => (state) => ({ usersList: [...state.usersList, user] })
};
Такой подход обеспечивает локализацию логики и упрощает масштабирование приложения.
Hyperapp поддерживает создание компонентов как функций, которые
принимают состояние и действия, а возвращают виртуальный DOM. Компоненты
можно хранить в отдельной папке components/ и использовать
в разных частях приложения.
Пример компонента Button:
export const Button = ({ onclick, label }) => (
<button oncl ick={onclick}>{label}</button>
);
Компоненты можно комбинировать в представлениях:
views/userView.js
import { Button } from '../components/Button.js';
export const userView = (state, actions) => (
<div>
<h2>Пользователи</h2>
{state.usersList.map(user => (
<div key={user.id}>
{user.name}
<Button label="Удалить" oncl ick={() => actions.removeUser(user.id)} />
</div>
))}
</div>
);
Hyperapp поддерживает композицию состояний и действий. Это позволяет объединять несколько модулей в единое глобальное состояние.
app.js
import { h, app } from "hyperapp";
import { state as userState } from "./state/user.js";
import { actions as userActions } from "./actions/userActions.js";
import { state as taskState } from "./state/tasks.js";
import { actions as taskActions } from "./actions/taskActions.js";
import { userView } from "./views/userView.js";
import { taskView } from "./views/taskView.js";
const state = {
user: userState,
tasks: taskState
};
const actions = {
user: userActions,
tasks: taskActions
};
const view = (state, actions) => (
<div>
{userView(state.user, actions.user)}
{taskView(state.tasks, actions.tasks)}
</div>
);
app({ init: state, view, actions, root: document.getElementById("app") });
Использование вложенной структуры состояния позволяет логически изолировать части приложения, при этом обеспечивая взаимодействие между модулями.
Hyperapp поддерживает эффекты (effects)
для выполнения асинхронных операций, таких как запросы к API. Эффекты
лучше оформлять в отдельных модулях, чтобы сохранять чистоту логики и не
смешивать их с синхронными действиями.
Пример эффекта:
export const fetchUsers = (dispatch) => {
fetch("/api/users")
.then(res => res.json())
.then(data => dispatch({ type: "setUsers", users: data }));
};
Действие может вызывать эффект:
export const actions = {
loadUsers: () => (_, actions) => [
{},
fetchUsers
]
};
Такой подход облегчает тестирование, так как эффекты могут быть заменены заглушками.
Представления рекомендуется делить по функциональным модулям и уровням вложенности. Например:
views/ — большие, независимые экраны или страницы.components/ — мелкие переиспользуемые элементы
интерфейса.Использование такой иерархии упрощает навигацию по проекту и повышает переиспользуемость кода.
Следование этим принципам делает Hyperapp-приложение масштабируемым, тестируемым и легко поддерживаемым, сохраняя простоту и легковесность, характерные для этого фреймворка.