Lit в React приложениях

Основы интеграции

Lit — современный фреймворк для создания Web Components, ориентированный на декларативное описание UI с высокой производительностью. В React-приложениях Lit-компоненты используются как нативные HTML-элементы, что позволяет сочетать преимущества React и возможности веб-компонентов: переиспользуемость, инкапсуляцию стилей и легковесность.

Для интеграции достаточно импортировать класс компонента и зарегистрировать его через customElements.define. После этого компонент можно использовать в JSX точно так же, как любой HTML-тег:

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

class MyButton extends LitElement {
  static styles = css`
    button {
      padding: 10px 20px;
      background-color: #6200ea;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }
  `;

  render() {
    return html`<button><slot></slot></button>`;
  }
}

customElements.define('my-button', MyButton);
function App() {
  return <my-button>Нажми меня</my-button>;
}

Передача свойств и событий

Lit-компоненты поддерживают реактивные свойства через декоратор @property. Для корректной работы с React важно учитывать, что React передаёт пропсы как обычные HTML-атрибуты, поэтому свойства должны быть синхронизированы с атрибутами:

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

class CounterElement extends LitElement {
  @property({ type: Number })
  count = 0;

  render() {
    return html`<div>Счётчик: ${this.count}</div>`;
  }
}

customElements.define('counter-element', CounterElement);

В React:

function App() {
  return <counter-element count={5}></counter-element>;
}

Для передачи событий используется CustomEvent. В React подключение событий через on<Event> работает только с кастомной обработкой, так как React не автоматически подписывается на addEventListener веб-компонентов:

class CounterElement extends LitElement {
  @property({ type: Number }) count = 0;

  increment() {
    this.count++;
    this.dispatchEvent(new CustomEvent('count-changed', { detail: this.count }));
  }

  render() {
    return html`<button @click=${this.increment}>${this.count}</button>`;
  }
}

customElements.define('counter-element', CounterElement);

В React обработка события:

function App() {
  const handleCountChange = (e) => {
    console.log('Новый счёт:', e.detail);
  };

  return <counter-element onCountChan ged={handleCountChange}></counter-element>;
}

Стилизация

Lit использует Shadow DOM для инкапсуляции стилей. Это позволяет избежать конфликтов с глобальными CSS React-приложения. Для передачи внешних стилей можно использовать CSS-переменные:

class StyledElement extends LitElement {
  static styles = css`
    div {
      color: var(--text-color, black);
      font-size: 16px;
    }
  `;

  render() {
    return html`<div>Текст с кастомной стилизацией</div>`;
  }
}

customElements.define('styled-element', StyledElement);

В React:

function App() {
  return <styled-element style={{ '--text-color': 'red' }}></styled-element>;
}

Использование React-хуков с Lit

Для интеграции реактивности React с Lit-компонентами можно использовать useRef для доступа к элементу и вызова его методов:

import { useRef, useEffect } from 'react';

function App() {
  const counterRef = useRef(null);

  useEffect(() => {
    if (counterRef.current) {
      counterRef.current.count = 10;
    }
  }, []);

  return <counter-element ref={counterRef}></counter-element>;
}

Это позволяет управлять внутренним состоянием веб-компонента из React и комбинировать реактивные механизмы обоих фреймворков.

Оптимизация производительности

Lit имеет минимальный overhead благодаря шаблонной системе и реактивным свойствам. В React-приложениях важно учитывать:

  • Избегать частого перерендеринга Lit-компонентов через React, так как обновления свойств уже происходят внутри веб-компонента.
  • Использовать React.memo для контейнеров с Lit-компонентами, если их состояние редко меняется.
  • Минимизировать передачу сложных объектов через атрибуты, использовать свойства через ref для глубоких структур данных.

Особенности совместимости

  • React автоматически не рендерит слот-контент веб-компонента как JSX; необходимо использовать <slot> внутри Lit.
  • Некоторые атрибуты React (className, htmlFor) должны быть заменены на стандартные HTML-атрибуты (class, for) при работе с Lit.
  • Поддержка серверного рендеринга (SSR) ограничена, так как веб-компоненты рендерятся только в браузере.

Использование Lit в React позволяет создавать высокопроизводительные и инкапсулированные компоненты с современными возможностями Web Components, сохраняя привычную архитектуру React-приложения.