repeat для эффективных списков

Библиотека Haunted — это современный инструмент для создания веб-компонентов на базе Web Components с реактивной моделью, похожей на React Hooks. Одной из ключевых задач при работе с динамическим контентом является эффективное отображение списков элементов. Для этого в Haunted используется специальная директива repeat, которая позволяет управлять списками без избыточного перерисовывания.


Основы директивы repeat

repeat импортируется из пакета haunted:

import { html, repeat } from 'haunted';

Его основная задача — оптимизация рендеринга массивов. Вместо того чтобы пересоздавать весь список при каждом обновлении состояния, repeat обновляет только те элементы, которые изменились, добавились или удалились. Это существенно снижает нагрузку на виртуальный DOM и улучшает производительность при больших списках.

Синтаксис:

repeat(array, keyFn, templateFn)
  • array — массив данных для рендеринга.
  • keyFn — функция, возвращающая уникальный ключ для каждого элемента массива. Ключ используется для отслеживания изменений конкретного элемента.
  • templateFn — функция, возвращающая HTML-разметку для каждого элемента.

Простейший пример:

const items = ['яблоко', 'банан', 'вишня'];

const template = html`
  <ul>
    ${repeat(items, item => item, item => html`<li>${item}</li>`)}
  </ul>
`;

В этом примере каждый элемент списка получает уникальный ключ, равный его значению. При изменении массива Haunted пересоздаст только те элементы, которые были добавлены или удалены, а не весь <ul> целиком.


Оптимизация через ключи

Правильный выбор ключей критически важен для производительности. Ключи должны быть:

  1. Уникальными в рамках текущего массива.
  2. Стабильными — не меняться при перестановках элементов.

Пример плохого ключа:

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 — это инструмент, который сочетает реактивность с высокой производительностью, делая работу со списками удобной и безопасной для больших объемов данных.