Lit — это современный фреймворк для создания веб-компонентов на основе стандарта Custom Elements. Центральной концепцией является компонент как самостоятельный элемент интерфейса, обладающий собственным состоянием и реактивной моделью обновлений. Для построения сложных интерфейсов необходимо грамотно организовать взаимодействие между компонентами.
Компоненты Lit обмениваются данными преимущественно через
свойства (properties). Свойства объявляются с помощью
декоратора @property:
import { LitElement, html, css } from 'lit';
import { property } from 'lit/decorators.js';
class UserCard extends LitElement {
@property({ type: String }) name = '';
@property({ type: Number }) age = 0;
render() {
return html`<div>${this.name}, ${this.age} лет</div>`;
}
}
customElements.define('user-card', UserCard);
При использовании компонента можно передавать значения через HTML-атрибуты:
<user-card name="Иван" age="30"></user-card>
Особенности:
type.Для уведомления родительского компонента о событиях в дочернем
компоненте используется CustomEvent. В Lit это оформляется
следующим образом:
class CounterButton extends LitElement {
@property({ type: Number }) count = 0;
increment() {
this.count++;
this.dispatchEvent(new CustomEvent('count-changed', {
detail: { count: this.count },
bubbles: true,
composed: true
}));
}
render() {
return html`<button @click=${this.increment}>${this.count}</button>`;
}
}
customElements.define('counter-button', CounterButton);
Ключевые моменты:
bubbles: true позволяет событию всплывать по
DOM-дереву.composed: true делает событие видимым за пределами
Shadow DOM.detail содержит полезную информацию о событии.На стороне родителя можно подписаться на событие:
<counter-button @count-changed=${e => console.log(e.detail.count)}></counter-button>
Слоты (<slot>) обеспечивают встраивание
внешнего контента внутрь компонента. Это позволяет создавать
гибкие и переиспользуемые компоненты:
class ModalDialog extends LitElement {
render() {
return html`
<div class="overlay">
<div class="content">
<slot></slot>
</div>
</div>
`;
}
}
customElements.define('modal-dialog', ModalDialog);
Использование:
<modal-dialog>
<h2>Заголовок модального окна</h2>
<p>Содержимое окна</p>
</modal-dialog>
Преимущества слотов:
Lit поддерживает реактивность на уровне свойств, что упрощает связь компонентов без прямой манипуляции DOM. Пример передачи состояния от родителя к дочернему компоненту:
class ParentComponent extends LitElement {
@property({ type: Number }) counter = 0;
render() {
return html`
<counter-button .count=${this.counter} @count-changed=${this.updateCounter}></counter-button>
`;
}
updateCounter(e) {
this.counter = e.detail.count;
}
}
customElements.define('parent-component', ParentComponent);
Важные моменты:
. перед свойством при передаче
(.count=${this.counter}) гарантирует передачу не
строки, а значения нужного типа.Для сложных интерфейсов часто используется комбинация входных свойств и выходных событий. Такой паттерн делает компоненты универсальными и независимыми:
Часто компоненты делятся на:
Пример:
class TodoListContainer extends LitElement {
@property({ type: Array }) todos = [];
addTodo(todo) {
this.todos = [...this.todos, todo];
}
render() {
return html`
<todo-list .items=${this.todos} @add-todo=${e => this.addTodo(e.detail)}></todo-list>
`;
}
}
class TodoList extends LitElement {
@property({ type: Array }) items = [];
render() {
return html`
<ul>
${this.items.map(item => html`<li>${item}</li>`)}
</ul>
<button @click=${() => this.dispatchEvent(new CustomEvent('add-todo', { detail: 'Новое задание' }))}>
Добавить
</button>
`;
}
}
customElements.define('todo-list-container', TodoListContainer);
customElements.define('todo-list', TodoList);
Преимущества:
Lit предоставляет гибкий набор инструментов для построения компонентных интерфейсов: реактивные свойства, события, слоты и комбинации этих механизмов позволяют создавать масштабируемые, модульные и легко сопровождаемые приложения. Правильное использование этих паттернов снижает связность между компонентами и повышает повторное использование кода.