Haunted — это легковесная библиотека для создания веб-компонентов с использованием функционального подхода, построенного на хуках, аналогичных React. Основная цель портирования компонентов в Haunted заключается в адаптации существующих приложений и компонентов под функциональную архитектуру Haunted, сохраняя при этом возможности переиспользования и реактивного обновления интерфейса.
В Haunted компонент определяется через функцию и декоратор
customElement. Базовая структура выглядит следующим
образом:
import { html, render } from 'haunted';
import { component } from 'haunted';
function MyComponent({ title }) {
return html`<h1>${title}</h1>`;
}
customElements.define('my-component', component(MyComponent));
Ключевые моменты:
html для шаблонной разметки.component оборачивает функцию для регистрации как
веб-компонента.При портировании существующих компонентов важно учитывать, что методы
жизненного цикла классовых компонентов заменяются хуками
(useState, useEffect,
useRef).
Существующие компоненты на основе классов можно переписать, используя функциональный подход. Рассмотрим пример:
Классовый компонент:
class Counter extends HTMLElement {
constructor() {
super();
this.count = 0;
}
connectedCallback() {
this.render();
}
increment() {
this.count++;
this.render();
}
render() {
this.innerHTML = `
<div>
<span>${this.count}</span>
<button id="inc">Increment</button>
</div>
`;
this.querySelector('#inc').addEventListener('click', () => this.increment());
}
}
customElements.define('x-counter', Counter);
Функциональный эквивалент на Haunted:
import { html } from 'haunted';
import { component } from 'haunted';
import { useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>
<span>${count}</span>
<button @click=${() => setCount(count + 1)}>Increment</button>
</div>
`;
}
customElements.define('x-counter', component(Counter));
Особенности портирования:
useState заменяет внутренние свойства и управление
состоянием.@click.innerHTML и
подпиской на события.Haunted предоставляет несколько ключевых хуков для управления состоянием и жизненным циклом:
Пример использования useEffect при портировании:
import { html } from 'haunted';
import { component } from 'haunted';
import { useState, useEffect } from 'haunted';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(interval);
}, []);
return html`<p>Прошло ${seconds} секунд</p>`;
}
customElements.define('x-timer', component(Timer));
Важные детали:
useEffect используется для очистки
ресурсов.[] имитирует
componentDidMount и componentWillUnmount.Haunted автоматически следит за изменением атрибутов и передает их в компонент как свойства. Для корректного портирования важно учитывать соответствие типов и синтаксиса:
function Greeting({ name = 'Гость' }) {
return html`<p>Привет, ${name}!</p>`;
}
customElements.define('x-greeting', component(Greeting, { observedAttributes: ['name'] }));
Особенности:
observedAttributes позволяет компоненту реагировать на
изменения атрибутов.Для компонентов с множеством состояний и логики полезно разделять функциональные блоки:
Пример разделения:
function TodoItem({ todo, onToggle }) {
return html`
<li>
<input type="checkbox" ?checked=${todo.done} @change=${onToggle}>
${todo.text}
</li>
`;
}
function TodoList({ todos }) {
const [items, setItems] = useState(todos);
const toggleItem = index => {
const newItems = [...items];
newItems[index].done = !newItems[index].done;
setItems(newItems);
};
return html`
<ul>
${items.map((todo, i) => html`<${TodoItem} .todo=${todo} .onTog gle=${() => toggleItem(i)} />`)}
</ul>
`;
}
customElements.define('x-todo-list', component(TodoList));
Преимущества такой структуры:
Haunted поддерживает использование сторонних библиотек, включая работу с DOM напрямую или с state-менеджерами. При портировании важно:
innerHTML вне Haunted.useEffect и useMemo.Haunted обеспечивает гибкость при портировании и облегчает переход с классовых компонентов на современный функциональный подход, сохраняя реактивность и удобство работы с веб-компонентами.