Haunted — это библиотека для создания веб-компонентов на основе Web Components и реактивного состояния в стиле React Hooks. Она позволяет интегрировать компоненты в любые окружения, сохраняя модульность и переиспользуемость кода. Особенность Haunted заключается в том, что его можно применять не только в чистом JavaScript, но и совместно с различными фреймворками, такими как React, Angular, Vue, и даже в проектах без фреймворка, используя стандартные DOM-API.
Haunted легко взаимодействует с React благодаря своей схожести с концепцией хуков.
Создание Haunted-компонента:
import { html, component, useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>
<p>Счёт: ${count}</p>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
</div>
`;
}
customElements.define('my-counter', component(Counter));
Использование внутри React-компонента:
import React from 'react';
function App() {
return (
<div>
<my-counter></my-counter>
</div>
);
}
export default App;
Ключевой момент — Haunted-компонент определяется как стандартный
веб-компонент через customElements.define. React просто
рендерит его как обычный DOM-элемент. При этом состояние компонента
полностью изолировано.
Angular поддерживает веб-компоненты через
CUSTOM_ELEMENTS_SCHEMA, что позволяет использовать
Haunted-компоненты без дополнительных обёрток.
Регистрация схемы в модуле:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import './my-counter.js'; // импорт Haunted-компонента
@NgModule({
declarations: [],
imports: [BrowserModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
bootstrap: []
})
export class AppModule {}
Использование в шаблоне:
<my-counter></my-counter>
Angular корректно обрабатывает такие элементы, а Haunted-компонент управляет своим состоянием самостоятельно.
Vue также поддерживает кастомные элементы. Для использования Haunted-компонентов достаточно зарегистрировать их как обычные HTML-теги:
import { createApp } from 'vue';
import './my-counter.js';
const app = createApp({
template: `<my-counter></my-counter>`
});
app.mount('#app');
Vue игнорирует Shadow DOM и внутреннее состояние Haunted-компонента, позволяя ему полностью автономно управлять интерфейсом. При необходимости можно передавать атрибуты или слушать события:
<my-counter count="5" @change="onCountChange"></my-counter>
Haunted особенно полезен в проектах без фреймворков, где важна реактивность без лишнего веса. Пример:
import { html, component, useState } from 'haunted';
function TodoList() {
const [todos, setTodos] = useState([]);
let input;
const addTodo = () => {
if (input.value) {
setTodos([...todos, input.value]);
input.value = '';
}
};
return html`
<div>
<input placeholder="Новая задача" ${ref(input)} />
<button @click=${addTodo}>Добавить</button>
<ul>
${todos.map(todo => html`<li>${todo}</li>`)}
</ul>
</div>
`;
}
customElements.define('todo-list', component(TodoList));
Здесь используется встроенная реактивность Haunted через
useState, а шаблон описан с помощью html из
библиотеки. Всё управление состоянием происходит локально в компоненте,
без глобальных стореджей или внешних библиотек.
Haunted-компоненты могут генерировать и слушать события, что облегчает взаимодействие с другими фреймворками:
const handleClick = () => {
const event = new CustomEvent('increment', { detail: count });
this.dispatchEvent(event);
};
При интеграции с Angular, React или Vue можно подписываться на эти события стандартными способами через атрибуты или слушатели событий.
CustomEvent
позволяет легко связывать компоненты между собой.useEffect,
useReducer) для управления побочными эффектами и более
сложным состоянием.Haunted сочетает в себе реактивность и лёгкость веб-компонентов, обеспечивая универсальный способ создавать переиспользуемые интерфейсы, которые можно интегрировать практически в любой стек фронтенда.