Haunted — это библиотека для создания веб-компонентов на основе функциональных реактивных хуков, вдохновлённая React. Для работы с шаблонами и динамическим обновлением DOM Haunted использует lit-html. Lit-html предоставляет мощный инструмент для декларативного описания интерфейса и эффективного обновления DOM без полного перерендеринга элементов.
В основе lit-html лежит концепция шаблонных
литералов JavaScript, где HTML-код оформляется внутри функции
html и может содержать динамические выражения.
import { html, render } from 'lit-html';
const template = (name) => html`
<div>
<h1>Привет, ${name}!</h1>
<p>Добро пожаловать в Haunted и lit-html.</p>
</div>
`;
render(template('Мир'), document.body);
Ключевые моменты:
html создаёт шаблонные строки, которые
internaly превращаются в дерево DOM-подобных узлов.${...} вставляются динамически, и
lit-html обновляет только изменённые узлы, минимизируя
операции над DOM.render(templateResult, container) отвечает за
монтирование шаблона в указанный DOM-элемент и его последующие
обновления.В Haunted компоненты определяются как функции, которые возвращают
шаблон lit-html. Состояние компонента управляется через хуки
useState, useEffect и другие.
import { component, useState } from 'haunted';
import { html } from 'lit-html';
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));
Разбор примера:
Counter — обычная функция, возвращающая
шаблон html.useState создаёт реактивное состояние. При вызове
setCount Haunted повторно рендерит компонент.@event=${handler}. Lit-html обеспечивает корректное
управление слушателями, предотвращая утечки памяти.Lit-html поддерживает рендеринг массивов и условные конструкции:
const items = ['Яблоко', 'Банан', 'Вишня'];
const listTemplate = html`
<ul>
${items.map(item => html`<li>${item}</li>`)}
</ul>
`;
render(listTemplate, document.body);
Array.map позволяет создавать
повторяющиеся элементы без ручного управления DOM.Условный рендеринг выполняется через тернарные операторы:
const loggedIn = true;
const loginTemplate = html`
<div>
${loggedIn ? html`<p>Добро пожаловать!</p>` : html`<button>Войти</button>`}
</div>
`;
render(loginTemplate, document.body);
Lit-html использует концепцию шаблонных инструкций, которые минимизируют манипуляции с DOM:
let count = 0;
const container = document.body;
function update() {
render(html`<p>Счётчик: ${count}</p>`, container);
}
setInterval(() => {
count++;
update();
}, 1000);
<p>, не трогая остальные узлы, что критически важно
для производительности.Lit-html позволяет безопасно задавать атрибуты и свойства элементов:
const isDisabled = true;
const buttonTemplate = html`
<button ?disabled=${isDisabled}>Нажми меня</button>
`;
render(buttonTemplate, document.body);
? используется для булевых атрибутов.. применяется для прямого присвоения свойства
элемента: <input .value=${inputValue}>.@ используется для событий:
<button @click=${handleClick}>.Комбинация Haunted и lit-html позволяет создавать высоко динамичные интерфейсы. Пример реактивного списка с состоянием:
import { component, useState } from 'haunted';
import { html } from 'lit-html';
function TodoList() {
const [todos, setTodos] = useState(['Сделать домашку', 'Купить продукты']);
const [newTodo, setNewTodo] = useState('');
const addTodo = () => {
if(newTodo.trim()) {
setTodos([...todos, newTodo]);
setNewTodo('');
}
};
return html`
<div>
<input .value=${newTodo} @input=${e => setNewTodo(e.target.value)} placeholder="Новая задача" />
<button @click=${addTodo}>Добавить</button>
<ul>
${todos.map(todo => html`<li>${todo}</li>`)}
</ul>
</div>
`;
}
customElements.define('todo-list', component(TodoList));
setTodos или
setNewTodo инициирует повторный рендер.html.Lit-html в сочетании с Haunted создаёт мощный инструмент для построения реактивных веб-компонентов, позволяя писать читаемый, эффективный и легко масштабируемый код.