Циклы и итерация по данным

В Lit управление динамическим контентом и повторяющимися элементами осуществляется с помощью встроенных возможностей шаблонов и директив. В отличие от традиционных подходов в чистом JavaScript, здесь нет необходимости вручную создавать элементы DOM для каждой записи — шаблоны автоматически обновляются при изменении данных. Основным инструментом для циклического рендеринга является директива repeat из пакета lit/directives/repeat.js.


Директива repeat

Директива repeat предназначена для оптимизированного рендеринга списков. Она принимает три аргумента:

  1. Массив элементов — данные для отображения.
  2. Функцию ключа — уникальный идентификатор элемента, который помогает Lit эффективно обновлять DOM при изменении массива.
  3. Функцию шаблона — возвращает HTML для каждого элемента.

Пример:

import { html, LitElement } from 'lit';
import { repeat } from 'lit/directives/repeat.js';

class ItemList extends LitElement {
  static properties = {
    items: { type: Array }
  };

  constructor() {
    super();
    this.items = [
      { id: 1, name: 'Элемент 1' },
      { id: 2, name: 'Элемент 2' },
      { id: 3, name: 'Элемент 3' }
    ];
  }

  render() {
    return html`
      <ul>
        ${repeat(
          this.items,
          item => item.id,
          item => html`<li>${item.name}</li>`
        )}
      </ul>
    `;
  }
}

customElements.define('item-list', ItemList);

В этом примере repeat гарантирует, что при изменении массива items обновятся только те элементы, которые реально изменились, что повышает производительность по сравнению с обычной генерацией массива через map.


Итерация с использованием map

Для простых случаев можно использовать метод массива map:

render() {
  return html`
    <ul>
      ${this.items.map(item => html`<li>${item.name}</li>`)}
    </ul>
  `;
}

Однако при изменении массива Lit пересоздаст все элементы списка, даже если изменился только один, что может быть менее эффективно для больших данных.


Условная итерация и фильтрация

Часто требуется отображать только часть массива. В Lit это реализуется с помощью комбинации фильтров массива и шаблонов:

render() {
  return html`
    <ul>
      ${this.items
        .filter(item => item.id % 2 === 0)
        .map(item => html`<li>${item.name}</li>`)}
    </ul>
  `;
}

Фильтруя данные до передачи в шаблон, можно создавать сложные динамические списки с минимальной нагрузкой на DOM.


Вложенные циклы

Вложенные циклы позволяют рендерить структуры типа таблиц или списков списков:

render() {
  const categories = [
    { name: 'Фрукты', items: ['Яблоко', 'Банан'] },
    { name: 'Овощи', items: ['Морковь', 'Помидор'] }
  ];

  return html`
    ${categories.map(category => html`
      <h3>${category.name}</h3>
      <ul>
        ${category.items.map(item => html`<li>${item}</li>`)}
      </ul>
    `)}
  `;
}

Каждый уровень шаблона обрабатывается отдельно, сохраняя реактивность и минимальные изменения DOM при обновлении данных.


Оптимизация производительности

  • Использование ключей в repeat: уникальные идентификаторы позволяют Lit точно определять, какие элементы были добавлены, удалены или изменены.
  • Деление больших массивов на части: рендеринг сразу тысяч элементов может быть тяжелым; стоит использовать виртуализацию или пагинацию.
  • Минимизация вложенных циклов: глубоко вложенные map могут создавать лишние вызовы рендера.

Реактивность и обновления данных

Lit автоматически отслеживает изменения свойств, объявленных в static properties. При обновлении массива через присвоение нового значения или метод splice шаблон будет повторно вычислен. Важно создавать новые массивы при изменении данных, чтобы Lit корректно детектировал изменения:

this.items = [...this.items, { id: 4, name: 'Элемент 4' }];

Использование методов, изменяющих массив на месте (push, pop) без создания нового массива может не вызывать перерендер.


Динамическая генерация атрибутов и событий

При итерации по данным можно динамически задавать атрибуты и события:

render() {
  return html`
    <ul>
      ${this.items.map(item => html`
        <li @click=${() => this.handleClick(item.id)}>
          ${item.name}
        </li>
      `)}
    </ul>
  `;
}

handleClick(id) {
  console.log('Выбран элемент:', id);
}

Каждый элемент получает индивидуальный обработчик, что упрощает управление событиями для больших списков.


Сравнение repeat и map

Параметр repeat map
Производительность Высокая при изменениях Средняя, перерисовка всех элементов
Поддержка ключей Да, уникальный идентификатор Нет
Простота кода Немного сложнее Очень просто
Применение Большие списки, часто обновляемые Небольшие статические списки

Использование циклов и итераций в Lit сочетает удобство декларативного подхода с высокой производительностью и реактивностью. Правильное применение repeat, фильтров, вложенных циклов и динамических обработчиков событий позволяет строить сложные интерфейсы с минимальными затратами ресурсов.