Модульная организация Hyperapp-приложений

Hyperapp представляет собой минималистичный JavaScript-фреймворк для построения веб-приложений с декларативным управлением состоянием и виртуальным DOM. Его компактность и простая архитектура позволяют строить приложения с высокой модульностью, разделяя код на независимые и повторно используемые части.


Структура приложения

В Hyperapp приложение обычно состоит из трёх ключевых компонентов:

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

Модульная организация предполагает раздельное хранение этих компонентов, что улучшает читаемость и тестируемость кода.

Пример структуры проекта:

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/ — мелкие переиспользуемые элементы интерфейса.

Использование такой иерархии упрощает навигацию по проекту и повышает переиспользуемость кода.


Резюме практик модульной организации

  1. Разделение состояния, действий и представлений на отдельные модули.
  2. Композиция состояний и действий для построения глобального состояния.
  3. Вынесение компонентов в отдельные файлы для переиспользования.
  4. Изоляция асинхронных операций через эффекты.
  5. Логическая группировка представлений и компонентов по функционалу.

Следование этим принципам делает Hyperapp-приложение масштабируемым, тестируемым и легко поддерживаемым, сохраняя простоту и легковесность, характерные для этого фреймворка.