JSX в Hyperapp и альтернативные подходы

JSX в Hyperapp используется как синтаксический слой над чисто функциональной моделью представления, не являясь обязательной частью фреймворка. Hyperapp изначально проектировался так, чтобы работать без JSX, однако поддержка JSX делает описание интерфейсов компактнее и выразительнее, не нарушая принципов минимализма.

В основе Hyperapp лежит функция h, создающая виртуальные узлы:

h("div", { class: "box" }, [
  h("h1", {}, "Заголовок"),
  h("p", {}, "Текст")
])

JSX транслируется в вызовы этой функции на этапе сборки:

<div class="box">
  <h1>Заголовок</h1>
  <p>Текст</p>
</div>

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

Настройка JSX для Hyperapp

Для корректной работы JSX требуется указать pragma — функцию, в которую будет компилироваться JSX. Для Hyperapp это h.

Пример конфигурации Babel:

{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "pragma": "h",
      "pragmaFrag": "Fragment"
    }]
  ]
}

Импорт в коде:

import { h } from "hyperapp"

Использование Fragment позволяет группировать элементы без дополнительного DOM-узла:

<>
  <li>Один</li>
  <li>Два</li>
</>

Функциональные компоненты

Компоненты в Hyperapp — это чистые функции, возвращающие виртуальное дерево:

const Button = (props) => (
  <button class={props.primary ? "primary" : ""}>
    {props.label}
  </button>
)

Компоненты не имеют собственного состояния и жизненного цикла в классическом смысле. Все данные передаются через аргументы, что делает JSX-компоненты предсказуемыми и легко тестируемыми.

Работа с состоянием и событиями в JSX

JSX напрямую связывается с экшенами Hyperapp:

<button oncl ick={state => ({ count: state.count + 1 })}>
  +
</button>

Обработчики событий получают состояние и возвращают новое состояние или пару [state, effect]. JSX не скрывает эту механику, а лишь упрощает запись.

Передача аргументов:

<button oncl ick={[increment, 5]}>+5</button>

Где increment — функция-экшен.

Условный рендеринг

JSX не вводит специальных директив. Используются стандартные выражения Jav * aScript:

{state.visible && <Modal />}

или

{state.items.length === 0
  ? <Empty />
  : <List items={state.items} />
}

Такой подход сохраняет прозрачность логики и не требует изучения дополнительных конструкций.

Списки и ключи

При рендеринге списков ключи указываются через атрибут key:

<ul>
  {state.todos.map(todo => (
    <li key={todo.id}>{todo.text}</li>
  ))}
</ul>

Ключи используются механизмом диффинга виртуального DOM и напрямую влияют на производительность и корректность обновлений.

JSX и SVG

JSX в Hyperapp корректно работает с SVG-элементами без дополнительных обёрток:

<svg width="100" height="100">
  <circle cx="50" cy="50" r="40" />
</svg>

Атрибуты передаются в исходном виде, без автоматических преобразований, как это происходит в React.

Ограничения JSX в контексте Hyperapp

JSX не решает архитектурные задачи и не заменяет модель данных. Он не добавляет:

  • локальное состояние компонентов;
  • хуки или эффекты;
  • сложную систему контекста.

Все эти решения остаются за пределами JSX и реализуются средствами самого Hyperapp.

Альтернатива: чистый h

Использование h без JSX устраняет зависимость от транспиляции и сборки:

h("button", { onclick: increment }, "+")

Преимущества:

  • отсутствие Babel;
  • полный контроль над структурой;
  • минимальный размер бандла.

Недостатки:

  • более громоздкий синтаксис;
  • снижение читаемости при сложной разметке.

Этот подход часто используется в средах с жёсткими ограничениями или при написании библиотек.

Альтернатива: htm

Библиотека htm предоставляет синтаксис, близкий к JSX, но без этапа компиляции:

import { h } from "hyperapp"
import htm from "htm"

const html = htm.bind(h)

html`
  <div>
    <h1>${title}</h1>
  </div>
`

Преимущества:

  • отсутствие сборки;
  • работа в браузере напрямую;
  • совместимость с Hyperapp.

Недостатки:

  • ошибки выявляются только во время выполнения;
  • меньшая поддержка инструментами анализа кода.

Альтернатива: шаблонные функции

Вместо JSX можно использовать обычные функции, возвращающие виртуальные узлы:

const view = (state) =>
  layout(
    header(state),
    content(state),
    footer(state)
  )

Такой стиль подчёркивает композицию и функциональное мышление, жертвуя визуальным сходством с HTML.

Выбор подхода

JSX в Hyperapp — это удобный, но необязательный инструмент. Он хорошо подходит для:

  • сложных интерфейсов;
  • командной разработки;
  • проектов с настроенной сборкой.

Альтернативы вроде h или htm лучше вписываются в минималистичные сценарии и подчёркивают философию Hyperapp как фреймворка без скрытой магии.