Система шаблонов lit-html

Основы шаблонов

В ядре Lit лежит библиотека lit-html, отвечающая за эффективное создание и обновление DOM через шаблоны. Шаблон в lit-html создается с помощью функции html, которая возвращает объект шаблона (TemplateResult). Основная особенность шаблонов — динамическое связывание данных с DOM, которое происходит без полной перерисовки дерева элементов.

import { html, render } from 'lit-html';

const name = 'Мир';
const template = html`<p>Привет, ${name}!</p>`;

render(template, document.body);

В примере ${name}интерполяция: при изменении значения переменной можно обновить только соответствующую часть DOM без пересоздания всего узла.

Привязка данных

Lit поддерживает разные типы привязки данных:

  • Текстовые выражения Используются внутри элементов для вставки строк или чисел:

    html`<span>${count}</span>`
  • Атрибуты и свойства элементов Для атрибутов применяется синтаксис .property или ?booleanAttribute:

    html`<input .value=${inputValue} ?disabled=${isDisabled}>`

    Здесь . указывает на установку свойства DOM, а ? — на булевый атрибут.

  • События Привязка обработчиков событий через @:

    html`<button @click=${handleClick}>Нажми</button>`

    Такой подход обеспечивает автоматическую очистку предыдущих слушателей при обновлении шаблона.

Динамическое управление структурой DOM

Для условного рендеринга и повторений используются функции ifDefined, repeat и встроенные конструкции Jav * aScript:

  • Условный рендеринг

    import { ifDefined } from 'lit-html/directives/if-defined.js';
    
    const template = html`<input value=${ifDefined(userInput)}>`
  • Повторение элементов

    Для списков применяется директива repeat, которая минимизирует изменения DOM:

    import { repeat } from 'lit-html/directives/repeat.js';
    
    const items = ['яблоко', 'банан', 'груша'];
    const listTemplate = html`
      <ul>
        ${repeat(items, item => item, item => html`<li>${item}</li>`)}
      </ul>
    `;

    Первый аргумент — массив, второй — ключевой идентификатор, третий — шаблон для элемента. Это позволяет сохранять состояние элементов при обновлении списка.

Комбинирование шаблонов

Шаблоны можно вложенно комбинировать, создавая модульные компоненты интерфейса:

const buttonTemplate = (label) => html`<button>${label}</button>`;

const panelTemplate = html`
  <div>
    ${buttonTemplate('Сохранить')}
    ${buttonTemplate('Отмена')}
  </div>
`;

Каждый вызов функции возвращает отдельный TemplateResult, который интегрируется в родительский шаблон.

Оптимизация рендеринга

Lit использует интеллектуальное сопоставление узлов: при повторном вызове render библиотека обновляет только изменившиеся части DOM. Это позволяет:

  • Избежать пересоздания неизменных элементов.
  • Сохранять состояние вложенных элементов (например, фокус, пользовательский ввод).
  • Минимизировать количество операций с DOM, что повышает производительность.
let count = 0;

function update() {
  const template = html`<p>Счёт: ${count}</p>`;
  render(template, document.body);
}

setInterval(() => {
  count++;
  update();
}, 1000);

Здесь перерисовывается только текст внутри <p>, а элемент <p> остаётся прежним.

Директивы и расширяемость

Lit поддерживает набор встроенных директив, которые управляют поведением шаблонов:

  • classMap и styleMap — динамическая установка классов и стилей.
  • guard — предотвращает ненужные обновления.
  • asyncAppend и asyncReplace — для асинхронного рендеринга коллекций.

Пример использования classMap:

import { classMap } from 'lit-html/directives/class-map.js';

const classes = { active: isActive, disabled: !isEnabled };
html`<div class=${classMap(classes)}>Контент</div>`

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

Поддержка шаблонов как компонентов

Lit строится на компонентной архитектуре. Каждый компонент обычно реализуется как класс, расширяющий LitElement, с методом render, который возвращает TemplateResult. Такой подход объединяет шаблоны и логику компонента, сохраняя ясность структуры кода:

import { LitElement, html } from 'lit';

class MyCounter extends LitElement {
  static properties = { count: { type: Number } };
  
  constructor() {
    super();
    this.count = 0;
  }

  render() {
    return html`
      <p>Счёт: ${this.count}</p>
      <button @click=${() => this.count++}>Увеличить</button>
    `;
  }
}

customElements.define('my-counter', MyCounter);

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

Интеграция с современными инструментами

Шаблоны lit-html легко интегрируются с:

  • Shadow DOM — для инкапсуляции стилей и структуры компонента.
  • CSS Result и css из Lit — для декларативного описания стилей.
  • Сторонними библиотеками — благодаря стандартной модели DOM и событий.

Эта гибкость делает систему шаблонов lit-html ядром современной реактивной архитектуры в JavaScript.