Haunted — это легковесная библиотека для создания веб-компонентов с реактивным состоянием на основе Hooks, вдохновленных React. Одной из ключевых особенностей Haunted является возможность интеграции серверного рендеринга (SSR) с веб-компонентами, что позволяет улучшить производительность и SEO.
В Haunted каждый компонент создаётся как стандартный веб-компонент. Это обеспечивает инкапсуляцию стилей и логики через Shadow DOM. Основной подход:
import { component, html, useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<button @click=${() => setCount(count + 1)}>
Счётчик: ${count}
</button>
`;
}
customElements.define('my-counter', component(Counter));
component() — функция, оборачивающая обычную функцию в
веб-компонент.html — шаблонизатор, использующий Tagged Template
Literals.useState — Hook для реактивного состояния внутри
компонента.Shadow DOM гарантирует, что стили и события компонента не конфликтуют с остальной страницей, что особенно важно при SSR, когда компонент может быть встроен в уже готовую HTML-разметку.
SSR позволяет рендерить веб-компоненты на сервере и
отдавать уже готовый HTML клиенту. В Haunted это реализуется через
render() из пакета haunted/ssr.
npm install haunted haunted/ssr
import { renderSSR } from 'haunted/ssr';
import { component, html, useState } from 'haunted';
function Greeting({ name }) {
return html`<p>Привет, ${name}!</p>`;
}
const GreetingComponent = component(Greeting);
const { html: ssrHtml, elements } = renderSSR(GreetingComponent, { name: 'Мир' });
console.log(ssrHtml); // <p>Привет, Мир!</p>
renderSSR возвращает объект с полями:
html — строка с готовым HTML.elements — массив виртуальных элементов для
последующего гидратационного процесса на клиенте.После SSR важно «оживить» компонент на клиенте:
import { component, html, useState } from 'haunted';
import { hydrate } from 'haunted/ssr';
function Greeting({ name }) {
const [count, setCount] = useState(0);
return html`
<p>Привет, ${name}! Кликнули ${count} раз.</p>
<button @click=${() => setCount(count + 1)}>Клик</button>
`;
}
customElements.define('my-greeting', component(Greeting));
const serverHtml = `<my-greeting name="Мир"></my-greeting>`;
hydrate(document.body, serverHtml);
hydrate связывает существующий DOM с реактивной
системой Haunted.В Haunted важно различать атрибуты и свойства веб-компонентов при SSR:
function UserCard({ user }) {
return html`<div>
<h2>${user.name}</h2>
<p>${user.email}</p>
</div>`;
}
customElements.define('user-card', component(UserCard));
// SSR
const { html: ssrHtml } = renderSSR(UserCard, { user: { name: 'Анна', email: 'anna@example.com' } });
Haunted поддерживает асинхронные хуки. Для SSR нужно учитывать, что сервер должен дождаться всех промисов перед выдачей HTML:
import { useState, useEffect } from 'haunted';
function AsyncData() {
const [data, setData] = useState(null);
useEffect(async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const json = await res.json();
setData(json);
}, []);
return html`${data ? html`<pre>${JSON.stringify(data)}</pre>` : html`Загрузка...`}`;
}
Shadow DOM изначально изолирует стили, но при SSR можно использовать:
const style = html`<style>
p { color: blue; font-weight: bold; }
</style>`;
function StyledParagraph({ text }) {
return html`${style}<p>${text}</p>`;
}
Haunted-компоненты легко вставляются в существующие SSR-фреймворки:
renderSSR для генерации HTML на сервере.Haunted позволяет создавать высокопроизводительные и изолированные компоненты, которые одинаково хорошо работают как на сервере, так и на клиенте, обеспечивая плавную гидратацию и реактивность состояния.