Рендеринг и обновление компонентов

Основы рендеринга

Lit предоставляет высокоэффективный механизм рендеринга, основанный на реактивных свойствах компонентов. Компонент, созданный с помощью LitElement, автоматически отслеживает изменения своих свойств и обновляет DOM только там, где это необходимо. Основной метод рендеринга — render(), который возвращает шаблон, созданный с использованием html-тегов из библиотеки lit.

import { LitElement, html, css } from 'lit';

class MyComponent extends LitElement {
  static properties = {
    message: { type: String }
  };

  render() {
    return html`<p>${this.message}</p>`;
  }
}

customElements.define('my-component', MyComponent);

В данном примере любое изменение свойства message автоматически инициирует повторный рендер только содержимого элемента <p>. Lit использует шаблонное обновление, что минимизирует количество изменений в DOM и повышает производительность.

Реактивные свойства

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

  • type: автоматически конвертирует значения при присвоении (например, String, Number, Boolean, Array, Object).
  • reflect: синхронизирует свойство с атрибутом элемента, позволяя использовать CSS-селекторы и наблюдать изменения через HTML.
  • hasChanged: позволяет задать кастомную функцию сравнения значений, контролируя, будет ли инициирован рендер.
static properties = {
  count: { type: Number, reflect: true, hasChanged: (newVal, oldVal) => newVal !== oldVal }
};

Если hasChanged возвращает false, рендер не выполняется, даже при изменении значения свойства.

Лайфсайкл обновления

Процесс обновления компонента в Lit делится на несколько этапов:

  1. Обновление свойств (update и requestUpdate) Метод requestUpdate инициирует проверку изменений и планирует рендер. При желании можно вручную вызвать обновление:

    this.requestUpdate('count', oldValue);
  2. Фаза подготовки (willUpdate) Метод willUpdate(changedProperties) вызывается перед рендером. Можно использовать для подготовки данных:

    willUpdate(changedProperties) {
      if (changedProperties.has('count')) {
        console.log('count изменился');
      }
    }
  3. Рендеринг (render) Метод возвращает шаблон, который будет отображён в Shadow DOM компонента. Lit использует lit-html для дифференцированного обновления DOM, минимизируя перерисовку.

  4. Фаза завершения (updated) Метод updated(changedProperties) вызывается после обновления DOM. Часто используется для запуска побочных эффектов, таких как интеграция с внешними библиотеками:

    updated(changedProperties) {
      if (changedProperties.has('count')) {
        console.log(`Новое значение count: ${this.count}`);
      }
    }

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

Для сложных компонентов с большим количеством данных Lit предлагает несколько способов оптимизации:

  • Ленивая загрузка шаблонов через динамические импорты.
  • Частичное обновление шаблона с использованием директив repeat, ifDefined и guard:
import { repeat } from 'lit/directives/repeat.js';

render() {
  return html`
    <ul>
      ${repeat(this.items, item => item.id, item => html`<li>${item.name}</li>`)}
    </ul>
  `;
}
  • Минимизация лишних вычислений через кеширование шаблонов или вычисляемых свойств.

Работа с атрибутами и внешними изменениями

Lit автоматически синхронизирует свойства и атрибуты, если указано reflect: true. Это упрощает интеграцию с HTML и позволяет использовать CSS-селекторы для изменения стилей в зависимости от состояния.

static properties = {
  active: { type: Boolean, reflect: true }
};

Установка this.active = true изменит атрибут active на <my-component active>, а удаление — на <my-component>.

Динамическая вставка и удаление элементов

Использование директив ifDefined и guard позволяет управлять рендером на уровне отдельных узлов, предотвращая лишние операции DOM:

import { guard } from 'lit/directives/guard.js';

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

Директива guard следит за зависимостями и пересоздаёт шаблон только при изменении массива items.

События и асинхронное обновление

Lit позволяет работать с событиями внутри компонентов без дополнительных библиотек:

render() {
  return html`<button @click=${this._handleClick}>Нажать</button>`;
}

_handleClick() {
  this.count++;
}

Событие изменяет реактивное свойство count, что инициирует повторный рендер. При этом обновление происходит асинхронно, что предотвращает лишние перерисовки при множественных изменениях.

Интеграция с CSS и Shadow DOM

Lit компоненты используют Shadow DOM по умолчанию, обеспечивая изоляцию стилей. Для динамических изменений стилей можно использовать свойства CSS или директиву styleMap:

import { styleMap } from 'lit/directives/style-map.js';

render() {
  const styles = { color: this.active ? 'red' : 'black' };
  return html`<p style=${styleMap(styles)}>Текст</p>`;
}

Это позволяет полностью контролировать отображение без прямого вмешательства в глобальные CSS.


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