Hyperapp использует концепцию виртуального DOM для оптимизации обновлений интерфейса. Вместо прямого взаимодействия с реальным DOM Hyperapp строит виртуальное дерево элементов, которое затем сравнивается с предыдущей версией. Этот процесс называется диффингом. Основное преимущество подхода — минимизация операций с реальным DOM, что повышает производительность приложений.
При создании элемента с помощью функции h формируется
виртуальный узел. Каждый узел содержит информацию о
теге, атрибутах, событиях и дочерних элементах. Когда вызывается
app, Hyperapp строит исходное виртуальное дерево и рендерит
его в DOM. Любые изменения состояния приложения инициируют создание
нового виртуального дерева и его сравнение с предыдущим.
import { h, app } from "hyperapp";
const state = { count: 0 };
const actions = {
increment: () => state => ({ count: state.count + 1 })
};
const view = (state, actions) =>
h("div", {}, [
h("h1", {}, `Счёт: ${state.count}`),
h("button", { onclick: actions.increment }, "Увеличить")
]);
app({ init: state, view, node: document.getElementById("app"), actions });
В этом примере изменение state.count вызывает
перерисовку только того узла, который изменился, без полной переработки
всего DOM.
В Hyperapp state является единственным источником правды. Любые изменения DOM должны происходить через изменение состояния. Это гарантирует предсказуемость и консистентность интерфейса.
Ключевые моменты:
const actions = {
toggle: () => state => ({ visible: !state.visible })
};
const view = (state, actions) =>
h("div", {}, [
h("button", { onclick: actions.toggle }, "Показать/Скрыть"),
state.visible && h("p", {}, "Этот текст виден только когда visible=true")
]);
Использование условного рендеринга позволяет отслеживать появление и исчезновение элементов без прямой работы с DOM.
Для работы с асинхронными процессами Hyperapp использует эффекты. Эффекты позволяют выполнять операции вне основной логики состояния, такие как запросы к серверу, таймеры или взаимодействие с локальным хранилищем.
const fetchData = () => (state, actions) => {
fetch("/data.json")
.then(res => res.json())
.then(data => actions.setData(data));
};
const actions = {
setData: data => state => ({ data }),
loadData: fetchData
};
Эффекты не изменяют state напрямую. Они вызывают действия, которые создают новый state, инициируя обновление виртуального DOM. Такой подход позволяет точно отслеживать все изменения интерфейса и сохранять его синхронизацию с состоянием приложения.
Hyperapp предоставляет механизм subscriptions, который позволяет реагировать на внешние события, не связанные напрямую с действиями пользователя. Подписки полезны для обработки событий клавиатуры, ресайза окна или потоков данных в реальном времени.
const subscriptions = state => [
window.addEventListener("resize", () => console.log(window.innerWidth))
];
Подписки интегрируются с жизненным циклом приложения и обеспечивают отслеживание изменений вне DOM, сохраняя консистентность состояния и интерфейса.
Hyperapp минимизирует обновления DOM через точечное сравнение узлов:
key) в списках
позволяет оптимизировать ререндеринг динамических коллекций
элементов.const view = (state, actions) =>
h("ul", {}, state.items.map(item =>
h("li", { key: item.id }, item.name)
));
Присвоение ключей гарантирует, что Hyperapp точно идентифицирует элементы списка, предотвращая лишние обновления и сохраняя текущее состояние компонентов.
Hyperapp позволяет использовать hooks для выполнения операций при монтировании, обновлении или удалении элементов:
oncreate: вызывается при создании DOM-узла.onupdate: вызывается при обновлении узла после
изменения состояния.ondestroy: вызывается перед удалением узла из DOM.h("div", {
oncreate: el => console.log("Создан элемент", el),
onupdate: el => console.log("Обновлён элемент", el),
ondestroy: el => console.log("Удалён элемент", el)
}, "Тест")
Эти хуки предоставляют тонкий контроль за жизненным циклом элементов и позволяют отслеживать изменения не только через state, но и на уровне реального DOM.
В Hyperapp обновления DOM полностью зависят от состояния. Использование виртуального DOM, эффектах, подписках и жизненном цикле узлов обеспечивает точное отслеживание изменений интерфейса и высокую производительность. Правильная структура состояния, иммутабельность и использование ключей позволяют создавать динамичные и отзывчивые приложения без прямой работы с DOM.