Внутреннее устройство Hyperapp

Hyperapp — это легковесный фронтенд-фреймворк для построения веб-приложений с минимальным объёмом кода. Его ядро строится на трёх ключевых концепциях: состояние (state), действия (actions) и вью (view). Взаимодействие этих компонентов формирует реактивную архитектуру, обеспечивая обновление интерфейса при изменении состояния.

State

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

Пример структуры состояния:

const state = {
  count: 0,
  user: {
    name: "Alex",
    loggedIn: false
  }
};

Actions

Действия — это функции, которые изменяют состояние приложения. Каждое действие принимает текущее состояние и возвращает новое состояние. Hyperapp позволяет использовать как синхронные, так и асинхронные действия.

Пример синхронного действия:

const actions = {
  increment: state => ({ count: state.count + 1 }),
  login: state => ({ user: { ...state.user, loggedIn: true } })
};

Для асинхронных действий Hyperapp использует эффекты (effects). Эффекты позволяют выполнять операции вне состояния, например, запросы к API:

const actions = {
  fetchUser: () => (state, actions) => {
    fetch("/api/user")
      .then(res => res.json())
      .then(data => actions.setUser(data));
  },
  setUser: (state, user) => ({ user })
};

View

View — это функция, которая принимает текущее состояние и действия, возвращает виртуальный DOM, описанный с помощью JSX-подобного синтаксиса. Hyperapp использует виртуальный DOM для эффективного обновления реального DOM, минимизируя количество операций.

Пример простого представления:

const view = (state, actions) => (
  h("div", {}, [
    h("h1", {}, `Счетчик: ${state.count}`),
    h("button", { onclick: actions.increment }, "Увеличить")
  ])
);

Виртуальный DOM и diffing

Hyperapp реализует собственный виртуальный DOM, который хранит дерево элементов в памяти. При изменении состояния происходит сравнение старого и нового дерева (diffing) с последующим обновлением только изменённых узлов. Этот механизм обеспечивает высокую производительность и минимальные операции с реальным DOM.

  • VNode — объект, описывающий элемент DOM (тип, свойства, дети).
  • Diffing — алгоритм сравнения старого и нового VNode.
  • Patching — применение изменений к реальному DOM.

Пример VNode:

const vnode = {
  tag: "div",
  props: { id: "container" },
  children: [
    { tag: "h1", props: {}, children: ["Привет, мир!"] }
  ]
};

Поддержка событий

Hyperapp использует события как часть VNode. События описываются как функции в свойствах элементов. При изменении состояния события автоматически пересвязываются, что предотвращает утечки памяти и гарантирует корректное поведение интерфейса.

h("button", { onclick: actions.increment }, "Увеличить")

Эффекты (Effects)

Эффекты в Hyperapp предназначены для работы с побочными действиями: HTTP-запросами, таймерами, localStorage. Эффекты отделены от чистых действий, что поддерживает функциональный стиль и предсказуемость состояния.

Структура эффекта:

const fetchEffect = (dispatch, props) => {
  fetch(props.url)
    .then(res => res.json())
    .then(data => dispatch(props.action, data));
};

const actions = {
  loadData: () => (state, actions) => fetchEffect(actions.setData, { url: "/api/data", action: actions.setData }),
  setData: (state, data) => ({ data })
};

Модульность и композиция

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

Пример модульного состояния:

const counterModule = {
  state: { count: 0 },
  actions: {
    increment: state => ({ count: state.count + 1 })
  }
};

const userModule = {
  state: { name: "", loggedIn: false },
  actions: {
    login: state => ({ loggedIn: true })
  }
};

const state = { ...counterModule.state, ...userModule.state };
const actions = { ...counterModule.actions, ...userModule.actions };

Поддержка JSX и hyperscript

Hyperapp позволяет использовать два подхода к созданию VNode: JSX и hyperscript (h-функция). Оба подхода эквивалентны, но JSX обеспечивает более компактный и читаемый синтаксис.

Hyperscript:

h("div", {}, [
  h("span", {}, "Текст")
])

JSX:

<div>
  <span>Текст</span>
</div>

Итоговая архитектура

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

Каждое обновление состояния инициирует diffing, вызывая пересчёт виртуального DOM и последующее минимальное обновление реального DOM, обеспечивая высокую производительность и предсказуемость поведения приложения.