Lit — это современный веб-фреймворк для создания легковесных компонентов с декларативным рендерингом. Ключевым аспектом его работы является реактивность, которая позволяет автоматически обновлять DOM при изменении состояния компонента. Основой этой реактивности в Lit служит наблюдаемый (observable) объект состояния и паттерн Observer.
В Lit реактивные свойства определяются с помощью декоратора
@property или через статическое поле
properties. Эти свойства автоматически отслеживают
изменения и вызывают повторный рендеринг компонента.
Пример:
import { LitElement, html } from 'lit';
import { property } from 'lit/decorators.js';
class MyCounter extends LitElement {
@property({ type: Number })
count = 0;
render() {
return html`
<div>Счётчик: ${this.count}</div>
<button @click=${() => this.count++}>Увеличить</button>
`;
}
}
customElements.define('my-counter', MyCounter);
В этом примере свойство count является реактивным. Любое
его изменение вызывает обновление шаблона. Это и есть применение
Observer-паттерна: компонент подписан на изменения
своих свойств, и DOM реагирует на эти изменения автоматически.
Lit позволяет использовать updated(changedProperties)
для реакции на изменения свойств. Метод вызывается после каждого
обновления компонента.
updated(changedProperties) {
if (changedProperties.has('count')) {
console.log(`Счётчик изменён: ${this.count}`);
}
}
Метод updated обеспечивает контроль над реактивностью,
позволяя выполнять побочные эффекты при изменении конкретных свойств.
Это прямое использование наблюдателя: компонент
наблюдает за изменением данных и реагирует на них.
Внутри Lit реактивные свойства реализуются через getter/setter. Когда свойство изменяется, срабатывает внутренний механизм, который ставит компонент в очередь на обновление. Это полностью соответствует принципам Observer:
Lit также поддерживает реактивные массивы и объекты через
ReactiveController или использование библиотек для
реактивных структур (например, @lit/reactive-element). Для
массивов и объектов важно создавать новые экземпляры при изменении,
чтобы наблюдатель корректно зафиксировал обновление.
Пример с массивом:
import { LitElement, html } from 'lit';
import { property } from 'lit/decorators.js';
class TodoList extends LitElement {
@property({ type: Array })
todos = [];
addTodo(todo) {
this.todos = [...this.todos, todo]; // создаем новый массив для триггера реакции
}
render() {
return html`
<ul>
${this.todos.map(todo => html`<li>${todo}</li>`)}
</ul>
<button @click=${() => this.addTodo('Новая задача')}>Добавить</button>
`;
}
}
customElements.define('todo-list', TodoList);
Создание нового массива является обязательным, потому что прямое
изменение существующего (push) не вызывает реактивного
обновления. Это связано с тем, что Observer следит за ссылкой на объект,
а не за его внутренними мутациями.
Для более сложных сценариев используется ReactiveController, который позволяет создавать отдельные логические блоки с реактивными зависимостями.
Пример:
import { ReactiveController, ReactiveControllerHost } from 'lit';
class LoggerController implements ReactiveController {
host;
constructor(host: ReactiveControllerHost) {
this.host = host;
host.addController(this);
}
hostUpdated() {
console.log('Компонент обновился', this.host);
}
}
Контроллер подписывается на жизненный цикл компонента и получает уведомления об обновлениях, что делает его расширением Observer-паттерна на уровне архитектуры компонента.
Главная ценность реактивности в Lit заключается в
декларативном рендеринге. Вместо прямого
манипулирования DOM с помощью querySelector и
innerHTML, изменения состояния автоматически отражаются в
интерфейсе. Это снижает сложность кода и исключает необходимость вручную
отслеживать обновления.
Ключевые моменты:
@property для реактивных
свойств.updated(changedProperties).Reactivity в Lit позволяет строить чистые, модульные и высокопроизводительные веб-компоненты, полностью опираясь на принципы Observer и декларативного рендеринга.