Библиотека Haunted — это современный инструмент для создания
веб-компонентов на базе Web Components с реактивной
моделью, похожей на React Hooks. Одной из ключевых задач при работе с
динамическим контентом является эффективное отображение списков
элементов. Для этого в Haunted используется специальная директива
repeat, которая позволяет управлять списками без
избыточного перерисовывания.
repeatrepeat импортируется из пакета haunted:
import { html, repeat } from 'haunted';
Его основная задача — оптимизация рендеринга массивов. Вместо того
чтобы пересоздавать весь список при каждом обновлении состояния,
repeat обновляет только те элементы, которые изменились,
добавились или удалились. Это существенно снижает нагрузку на
виртуальный DOM и улучшает производительность при больших списках.
Синтаксис:
repeat(array, keyFn, templateFn)
Простейший пример:
const items = ['яблоко', 'банан', 'вишня'];
const template = html`
<ul>
${repeat(items, item => item, item => html`<li>${item}</li>`)}
</ul>
`;
В этом примере каждый элемент списка получает уникальный ключ, равный
его значению. При изменении массива Haunted пересоздаст только те
элементы, которые были добавлены или удалены, а не весь
<ul> целиком.
Правильный выбор ключей критически важен для производительности. Ключи должны быть:
Пример плохого ключа:
repeat(items, (item, index) => index, item => html`<li>${item}</li>`);
Использование индекса в качестве ключа приводит к тому, что при изменении массива Haunted может пересоздать элементы, которые фактически не изменились. Лучше использовать уникальные идентификаторы элементов:
const users = [
{ id: 1, name: 'Анна' },
{ id: 2, name: 'Борис' },
{ id: 3, name: 'Виктор' }
];
html`
<ul>
${repeat(users, user => user.id, user => html`<li>${user.name}</li>`)}
</ul>
`;
Здесь каждый элемент отслеживается по id, что
обеспечивает корректное обновление при добавлении, удалении или
изменении пользователя.
repeat особенно эффективен при работе с
динамическими списками, которые могут изменяться в
реальном времени. Рассмотрим пример добавления и удаления элементов:
import { component, html, useState } from 'haunted';
import { repeat } from 'haunted';
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, task: 'Выучить Haunted' },
{ id: 2, task: 'Создать компонент' }
]);
const addTodo = () => {
const id = todos.length + 1;
setTodos([...todos, { id, task: `Новое задание ${id}` }]);
};
const removeTodo = id => {
setTodos(todos.filter(todo => todo.id !== id));
};
return html`
<button @click=${addTodo}>Добавить задание</button>
<ul>
${repeat(
todos,
todo => todo.id,
todo => html`
<li>
${todo.task}
<button @click=${() => removeTodo(todo.id)}>Удалить</button>
</li>
`
)}
</ul>
`;
}
customElements.define('todo-list', component(TodoList));
При каждом изменении массива пересоздаются только новые элементы или удаляются удалённые. Существующие элементы остаются нетронутыми.
repeat можно использовать не только для простых
<li>, но и для вложенных структур:
const categories = [
{ id: 1, name: 'Фрукты', items: ['яблоко', 'банан'] },
{ id: 2, name: 'Овощи', items: ['морковь', 'помидор'] }
];
html`
<div>
${repeat(
categories,
cat => cat.id,
cat => html`
<h3>${cat.name}</h3>
<ul>
${repeat(cat.items, item => item, item => html`<li>${item}</li>`)}
</ul>
`
)}
</div>
`;
Каждый уровень вложенности может использовать собственный
repeat с уникальными ключами. Это позволяет эффективно
рендерить сложные и динамичные структуры данных.
repeat
не обязателен, но при больших динамических списках он критичен для
производительности.repeat на
каждом уровне.id или уникальный идентификатор, а не индекс.repeat в Haunted — это инструмент, который сочетает
реактивность с высокой производительностью, делая работу со списками
удобной и безопасной для больших объемов данных.