Module Federation

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

Определение компонента

Каждый компонент в Lit создаётся через наследование от LitElement:

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

class MyComponent extends LitElement {
  static properties = {
    name: { type: String },
    age: { type: Number }
  };

  static styles = css`
    p {
      color: blue;
      font-weight: bold;
    }
  `;

  constructor() {
    super();
    this.name = 'Имя';
    this.age = 25;
  }

  render() {
    return html`
      <p>Привет, ${this.name}. Тебе ${this.age} лет.</p>
    `;
  }
}

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

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

  • static properties — реактивные свойства компонента. Любое их изменение автоматически инициирует перерисовку шаблона.
  • static styles — встроенные стили компонента. Они применяются только к его теневому DOM.
  • Метод render() возвращает шаблон в формате html, поддерживающий выражения ${} для динамических данных.
  • Регистрация через customElements.define делает компонент доступным в HTML.

Жизненный цикл компонента

Lit использует стандартные методы Web Components:

  • constructor() — инициализация начальных свойств и состояния.
  • connectedCallback() — вызывается при добавлении компонента в DOM.
  • disconnectedCallback() — вызывается при удалении компонента из DOM.
  • firstUpdated(changedProperties) — вызывается после первого рендера, подходит для настройки сторонних библиотек или подписок.
firstUpdated() {
  console.log('Компонент отрендерен впервые');
}

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

Реактивные свойства автоматически отслеживаются Lit, что позволяет минимизировать ручные манипуляции DOM. Изменение свойства вызывает повторный рендер:

this.name = 'Новый пользователь'; // автоматически обновит шаблон

Для более сложных случаев можно использовать requestUpdate:

this.requestUpdate('age', oldValue);

requestUpdate позволяет вручную инициировать обновление, указывая старое значение свойства для оптимизации.

Стилизация и теневой DOM

Lit использует теневой DOM для изоляции стилей. Компонент не “протекает” вовне и не подвержен внешним стилям. Стили можно определять через static styles или через CSSResult:

import { css } from 'lit';

static styles = css`
  :host {
    display: block;
    margin: 10px 0;
  }
  p {
    color: green;
  }
`;
  • :host — селектор самого компонента.
  • Локальные стили применяются только внутри компонента, что обеспечивает модульность и предсказуемость визуального отображения.

События и взаимодействие компонентов

Lit позволяет легко создавать пользовательские события и слушать их. Используется стандартный CustomEvent:

this.dispatchEvent(new CustomEvent('user-changed', {
  detail: { name: this.name },
  bubbles: true,
  composed: true
}));
  • bubbles позволяет событию всплывать.
  • composed разрешает событию пересекать границы теневого DOM.
  • Слушатели событий можно добавлять как через HTML, так и через JS:
<my-component @user-changed=${this.handleUserChanged}></my-component>

Шаблоны и условный рендеринг

Lit поддерживает мощные механизмы работы с шаблонами: условные выражения, циклы и фрагменты.

Условный рендеринг:

render() {
  return html`
    ${this.age >= 18 ? html`<p>Взрослый</p>` : html`<p>Ребёнок</p>`}
  `;
}

Циклы:

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

Использование html и map обеспечивает реактивное обновление списков без полного перерендера DOM.

Интеграция с Module Federation

Module Federation позволяет разделять приложения на микрофронтенды и загружать отдельные модули динамически. Lit-компоненты идеально подходят для таких сценариев, так как они независимы и могут быть внедрены в любой контейнер.

Пример экспорта Lit-компонента через Module Federation:

// webpack.config.js
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'litApp',
      filename: 'remoteEntry.js',
      exposes: {
        './MyComponent': './src/MyComponent.js'
      },
      shared: { 
        lit: { singleton: true }
      }
    })
  ]
};

Динамическая загрузка в другом приложении:

import('litApp/MyComponent').then(module => {
  const MyComponent = module.default;
  customElements.define('remote-component', MyComponent);
});
  • Компонент остаётся полностью изолированным.
  • Общие зависимости (например, lit) можно подключать как синглтоны, чтобы избежать дублирования.
  • Module Federation обеспечивает масштабирование больших проектов через микрофронтенды с минимальной связностью.

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

Lit минимизирует работу с DOM за счёт:

  • Реактивных свойств, вызывающих перерендер только изменённых частей.
  • Шаблонной привязки через html, которая использует виртуальные узлы.
  • Возможности ленивой загрузки компонентов через динамический импорт.

Для больших приложений рекомендуется комбинировать Module Federation и ленивую загрузку, чтобы компоненты загружались только при необходимости, сокращая время первоначальной загрузки.

Итоги использования Lit в микрофронтендах

Lit сочетает:

  • Лёгкость создания веб-компонентов.
  • Полную изоляцию через теневой DOM.
  • Интеграцию с современными сборщиками и микрофронтенд-архитектурами.
  • Высокую производительность за счёт реактивности и оптимизированного рендера.

Его архитектура позволяет строить масштабируемые и модульные интерфейсы, где каждый компонент может существовать независимо и взаимодействовать с другими частями приложения через стандартизированные события и Module Federation.