Hyperapp — это минималистичный фреймворк для построения реактивных пользовательских интерфейсов, ориентированный на небольшие и быстрые приложения. Одним из важных аспектов современных веб-приложений является Server-Side Rendering (SSR), позволяющий формировать HTML на сервере до отправки клиенту. SSR повышает скорость загрузки, улучшает SEO и обеспечивает начальное отображение страницы без ожидания загрузки JavaScript на клиенте.
Hyperapp, благодаря своей легковесной архитектуре, позволяет
реализовать SSR достаточно просто. Важная особенность —
состояние приложения и функции отображения
(view) должны быть детерминированы, чтобы серверный рендер
точно соответствовал клиентскому.
Для работы SSR необходимо разделить логику приложения на три ключевых компонента:
import { h, app } from "hyperapp";
const state = {
count: 0
};
const actions = {
increment: value => state => ({ count: state.count + value })
};
const view = (state, actions) =>
h("div", {}, [
h("h1", {}, `Счётчик: ${state.count}`),
h("button", { onclick: () => actions.increment(1) }, "Увеличить")
]);
Hyperapp предоставляет функцию app для запуска
приложения на клиенте, однако для SSR нужно использовать отдельный
подход — генерацию HTML с помощью h и преобразование
виртуального DOM в строку. Это достигается через утилиты вроде
hyperapp-render или кастомной функции.
Простейший пример рендеринга HTML на сервере:
import { h } from "hyperapp";
import { render } from "hyperapp-render"; // библиотека для рендера на сервере
const state = { count: 5 };
const view = (state) =>
h("div", {}, [
h("h1", {}, `Счётчик: ${state.count}`)
]);
const html = render(view(state));
console.log(html); // <div><h1>Счётчик: 5</h1></div>
Ключевой момент: серверный рендер возвращает чистый HTML, который затем можно вставить в шаблон страницы и отправить клиенту.
Для полноценного SSR Hyperapp часто интегрируется с сервером Node.js. Пример маршрута Express с генерацией HTML:
import express from "express";
import { h } from "hyperapp";
import { render } from "hyperapp-render";
const appServer = express();
const state = { count: 10 };
const view = state => h("div", {}, `Счётчик: ${state.count}`);
appServer.get("/", (req, res) => {
const html = render(view(state));
res.send(`
<!DOCTYPE html>
<html lang="ru">
<head><title>Hyperapp SSR</title></head>
<body>${html}</body>
</html>
`);
});
appServer.listen(3000);
Особенность: сервер рендерит страницу один раз при запросе, формируя начальный HTML. Клиентский JavaScript затем «гидратирует» страницу, добавляя интерактивность.
После SSR сервер отправляет готовый HTML. Для сохранения реактивности Hyperapp требуется гидратация, то есть связывание уже отрендеренного DOM с клиентским состоянием.
import { app } from "hyperapp";
const state = { count: 10 };
const actions = {
increment: value => state => ({ count: state.count + value })
};
const view = (state, actions) =>
h("div", {}, [
h("h1", {}, `Счётчик: ${state.count}`),
h("button", { onclick: () => actions.increment(1) }, "Увеличить")
]);
app({ init: state, view, node: document.body, actions });
Важно использовать одно и то же начальное состояние на сервере и клиенте, чтобы DOM, созданный сервером, точно совпадал с виртуальным DOM на клиенте.
Реальные приложения редко обходятся без асинхронных данных. Для SSR важно загрузить данные на сервере перед рендером, чтобы пользователь получил полностью сформированную страницу.
const fetchData = async () => {
const response = await fetch("https://api.example.com/data");
return await response.json();
};
appServer.get("/", async (req, res) => {
const data = await fetchData();
const html = render(state => h("div", {}, `Данные: ${JSON.stringify(data)}`));
res.send(html);
});
Преимущество: страница сразу содержит данные, что ускоряет отображение и улучшает SEO.
Для многостраничных приложений SSR в Hyperapp часто используют собственную маршрутизацию. Сервер обрабатывает URL и рендерит соответствующий view:
const routes = {
"/": () => h("h1", {}, "Главная"),
"/about": () => h("h1", {}, "О нас")
};
appServer.get("*", (req, res) => {
const viewFn = routes[req.path] || (() => h("h1", {}, "404"));
const html = render(viewFn());
res.send(html);
});
Маршрутизация на клиенте затем синхронизирует состояние, чтобы ссылки оставались интерактивными без перезагрузки страницы.
Для увеличения производительности SSR-страниц полезно:
Эти меры позволяют Hyperapp эффективно работать в SSR-режиме даже для сложных приложений.
Hyperapp с SSR остаётся лёгким, быстрым и прозрачным инструментом, позволяющим создавать современные веб-приложения с минимальными накладными расходами.