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

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


Объявление реактивных свойств

Реактивные свойства в Lit создаются с помощью статического геттера properties в классе компонента:

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

class MyComponent extends LitElement {
  static properties = {
    name: { type: String },
    age: { type: Number },
    isActive: { type: Boolean }
  };
  
  constructor() {
    super();
    this.name = 'Иван';
    this.age = 25;
    this.isActive = true;
  }
  
  render() {
    return html`
      <p>Имя: ${this.name}</p>
      <p>Возраст: ${this.age}</p>
      <p>Активен: ${this.isActive}</p>
    `;
  }
}

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

Ключевые моменты:

  • Свойства объявляются в объекте properties.
  • Поле type определяет тип данных и влияет на сериализацию при работе с атрибутами.
  • Инициализация может происходить в конструкторе.

Типы реактивных свойств

Lit поддерживает несколько типов данных, которые обеспечивают корректную конвертацию между атрибутами и свойствами:

  • String — текстовые значения.
  • Number — числовые значения, автоматически преобразуются из строки атрибута.
  • Boolean — логические значения, присутствие атрибута трактуется как true.
  • Array / Object — сложные структуры, требуют осторожности при обновлении (обычно используется клонирование для триггера реактивного обновления).

Обновление свойств и рендеринг

Любое изменение реактивного свойства автоматически инициирует повторный рендер компонента. Это работает через сеттеры, генерируемые Lit:

this.name = 'Пётр'; // автоматически вызовет обновление render()
this.age += 1;       // изменится значение в DOM

Особенности обновления:

  • Рендеринг выполняется асинхронно, в следующем цикле микротасков.
  • Несколько последовательных изменений свойств объединяются, чтобы минимизировать перерисовку.

Кастомизация реакции на изменение свойств

Можно перехватывать изменения через метод updated(changedProperties):

updated(changedProperties) {
  if (changedProperties.has('age')) {
    console.log(`Возраст изменился с ${changedProperties.get('age')} на ${this.age}`);
  }
}
  • changedProperties — карта предыдущих значений свойств.
  • Метод вызывается после завершения рендеринга.

Для контроля, будет ли обновление рендерить компонент, используется метод shouldUpdate(changedProperties):

shouldUpdate(changedProperties) {
  return changedProperties.has('name'); // рендер только если изменилось имя
}

Связь свойств с атрибутами

Реактивные свойства могут быть связаны с HTML-атрибутами, что позволяет задавать начальные значения в разметке:

<my-component name="Анна" age="30"></my-component>
  • Boolean-атрибуты: <my-component is-active></my-component>isActive = true.
  • Преобразование типов осуществляется автоматически в соответствии с type.

Реактивные свойства для сложных структур

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

this.items = [...this.items, newItem]; // триггер обновления
this.settings = { ...this.settings, theme: 'dark' };
  • Прямое изменение внутренних полей массива или объекта (this.items.push(...)) не вызовет перерисовку.

Декораторы и синтаксис TypeScript

Lit поддерживает использование декораторов для упрощения объявления свойств:

import { LitElement, html } from 'lit';
import { property } from 'lit/decorators.js';

class MyComponent extends LitElement {
  @property({ type: String }) name = 'Иван';
  @property({ type: Number }) age = 25;

  render() {
    return html`<p>${this.name} - ${this.age}</p>`;
  }
}
  • Декораторы позволяют объединить объявление и инициализацию.
  • Часто используются в проектах на TypeScript.

Оптимизация реактивных обновлений

  • Избегать избыточного рендеринга через shouldUpdate.
  • Использовать локальные свойства для данных, которые не участвуют в рендере.
  • Клонировать объекты и массивы при их изменении.
  • Группировать изменения свойств в один цикл микротасков.

Практическое применение

Реактивные свойства позволяют строить сложные интерфейсы с минимальной ручной работой:

  • Таблицы с динамическими данными.
  • Формы с валидацией и состоянием.
  • Компоненты с анимацией и состоянием видимости.
  • Связь нескольких компонентов через общий объект состояния.

Эта система обеспечивает простоту и предсказуемость реактивного обновления без необходимости внешних библиотек управления состоянием.