HTML Templates и элемент template

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

Шаблон представляет собой неизменяемую часть (статическую разметку) и набор динамических выражений. Lit один раз анализирует структуру шаблона и в дальнейшем обновляет только те места, где изменились значения выражений.

import { html } from 'lit';

const template = html`
  <p>Текущее значение: ${value}</p>
`;

Выражение внутри ${} может содержать любое JavaScript-значение: примитив, объект, массив, функцию или другой шаблон.


Теговая функция html

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

Ключевые свойства шаблонов Lit:

  • Иммутабельность — структура шаблона не меняется после создания.
  • Повторное использование — один и тот же шаблон может применяться многократно.
  • Оптимизированный рендеринг — Lit обновляет только изменившиеся части.

В отличие от строковой конкатенации HTML, шаблоны Lit защищены от XSS по умолчанию, так как значения автоматически экранируются.


Связь шаблонов и рендеринга

Шаблон сам по себе ничего не отображает. Он передаётся в механизм рендеринга, который связывает его с DOM. В контексте веб-компонентов Lit это происходит внутри метода render().

render() {
  return html`
    <h1>${this.title}</h1>
  `;
}

Каждый вызов render() возвращает новый TemplateResult, но Lit сравнивает его со старым и выполняет минимальное количество изменений в DOM.


Динамические выражения в шаблонах

Выражения могут использоваться практически в любом месте HTML-разметки:

Текстовое содержимое

html`<span>${message}</span>`

Атрибуты

html`<input value=${this.value}>`

Если значение равно null или undefined, атрибут будет удалён.

Булевы атрибуты

Для булевых атрибутов используется префикс ?.

html`<button ?disabled=${this.loading}>`

Атрибут будет присутствовать только при true.

Свойства DOM-элементов

Для установки свойств используется префикс ..

html`<input .value=${this.value}>`

Это важно для работы с объектами, массивами и нестандартными типами данных.


Обработчики событий в шаблонах

Lit использует нативную модель событий браузера. Обработчики указываются через префикс @.

html`
  <button @click=${this.handleClick}>
    Нажать
  </button>
`

Функция-обработчик передаётся по ссылке, без вызова. Контекст this автоматически привязывается к экземпляру компонента.


Вложенные шаблоны

Шаблоны можно вкладывать друг в друга без дополнительных обёрток.

const itemTemplate = (item) => html`
  <li>${item}</li>
`;

html`
  <ul>
    ${items.map(itemTemplate)}
  </ul>
`

Lit корректно обрабатывает массивы шаблонов, создавая и обновляя список элементов.


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

Для условного отображения используются обычные тернарные операторы или логические выражения.

html`
  ${this.visible
    ? html`<p>Отображается</p>`
    : html`<p>Скрыто</p>`
  }
`

Если результат выражения равен null, undefined или false, соответствующая часть DOM не будет создана.


Специальный элемент <template>

HTML-элемент <template> — это стандартный механизм браузера для хранения фрагментов разметки, которые не отображаются сразу. Его содержимое не участвует в рендеринге и не влияет на DOM, пока не будет явно использовано.

<template id="card-template">
  <div class="card">
    <h2></h2>
    <p></p>
  </div>
</template>

Содержимое <template> доступно через свойство content, представляющее DocumentFragment.


Отличия <template> от шаблонов Lit

Несмотря на схожее название, <template> и шаблоны Lit решают разные задачи:

  • <template> — это низкоуровневый API браузера
  • html в Lit — декларативное описание UI с реактивностью

Основные различия:

Критерий <template> Lit html
Реактивность отсутствует встроена
Обновление DOM вручную автоматически
Синтаксис чистый HTML HTML + JS
Производительность зависит от реализации оптимизирована

Использование <template> внутри Lit

Элемент <template> может применяться в Lit, но чаще всего для интеграции со сторонними библиотеками или при работе с сырым DOM.

firstUpdated() {
  const tpl = this.renderRoot.querySelector('#tpl');
  const clone = tpl.content.cloneNode(true);
  this.renderRoot.appendChild(clone);
}

В таких случаях Lit не отслеживает изменения внутри вставленного фрагмента, и ответственность за обновление ложится на разработчика.


Почему Lit не использует <template> напрямую

Lit создаёт собственное представление шаблона, поскольку стандартный <template>:

  • не поддерживает выражения JavaScript;
  • не умеет связываться с состоянием;
  • не оптимизирует повторные обновления.

Lit анализирует строки шаблона, создаёт карту динамических частей и затем использует DocumentFragment и NodePart для точечного обновления DOM.


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

Шаблоны Lit кэшируются автоматически. Если два шаблона имеют одинаковую структуру строк, они используют один и тот же внутренний Template.

html`<p>${a}</p>`
html`<p>${b}</p>`

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


Ограничения шаблонов

Некоторые ограничения принципиальны:

  • нельзя динамически менять теги (<${tag}> недопустимо);
  • структура DOM должна быть известна заранее;
  • нельзя вставлять HTML-строки напрямую без явного указания.

Для вставки доверенного HTML используется директива unsafeHTML, что подчёркивает потенциальную опасность операции.


Роль шаблонов в архитектуре Lit

HTML-шаблоны являются центральным элементом Lit:

  • связывают состояние и представление;
  • обеспечивают предсказуемый рендеринг;
  • позволяют писать UI как чистую функцию от данных.

Элемент <template> остаётся вспомогательным инструментом, тогда как шаблоны Lit формируют декларативный, реактивный и высокопроизводительный слой представления.