Micro-frontends

Slim.js — это лёгкая библиотека для создания веб-компонентов, ориентированная на производительность и минимальный размер кода. Она обеспечивает декларативный подход к разработке интерфейсов, позволяя определять компоненты как на уровне шаблонов HTML, так и через JavaScript-классы. Основной концепцией является связь данных и представления с минимальным вмешательством в DOM.

Особенности Slim.js:

  • Лёгкий вес (несколько килобайт).
  • Поддержка реактивных данных.
  • Простое создание кастомных элементов через Slim.tag.
  • Интеграция с существующими HTML-шаблонами.

Создание компонентов

Для определения нового компонента используется функция Slim.tag. Синтаксис следующий:

Slim.tag('my-component', class extends Slim {
  get template() {
    return `
      <div>
        <h1>{{title}}</h1>
        <p>{{description}}</p>
      </div>
    `;
  }

  onBeforeCreated() {
    this.title = 'Заголовок компонента';
    this.description = 'Описание компонента';
  }
});
  • template возвращает строку с HTML-шаблоном, где данные оборачиваются двойными фигурными скобками {{ }}.
  • onBeforeCreated позволяет инициализировать реактивные свойства до монтирования в DOM.
  • Любое свойство, объявленное внутри класса, автоматически становится реактивным, если оно используется в шаблоне.

Реактивность и связывание данных

Slim.js реализует одностороннюю реактивность: изменения свойств автоматически отражаются в DOM. Для обновления данных достаточно присвоить новое значение свойству:

this.title = 'Новый заголовок';

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

this.observe('title', (newValue, oldValue) => {
  console.log(`Title изменён с ${oldValue} на ${newValue}`);
});

События и пользовательская логика

Slim.js позволяет удобно работать с событиями:

<button click="increment()">Нажать</button>
<span>{{counter}}</span>
Slim.tag('counter-component', class extends Slim {
  onBeforeCreated() {
    this.counter = 0;
  }

  increment() {
    this.counter++;
  }
});

Метод increment автоматически связывается с атрибутом click в шаблоне. Это обеспечивает чистую декларативную привязку событий без использования addEventListener.

Работа с жизненным циклом компонентов

Slim.js предоставляет несколько хуков жизненного цикла:

  • onBeforeCreated — перед созданием элемента, инициализация данных.
  • onAfterCreated — после создания элемента, можно выполнять DOM-операции.
  • onBeforeUpdate — перед обновлением DOM при изменении реактивных свойств.
  • onAfterUpdate — после обновления DOM, полезно для сторонней интеграции.

Пример использования:

Slim.tag('lifecycle-component', class extends Slim {
  onBeforeCreated() {
    this.text = 'Инициализация';
  }

  onAfterCreated() {
    console.log('Компонент создан');
  }

  onAfterUpdate() {
    console.log('DOM обновлён, текущее значение:', this.text);
  }
});

Поддержка слотов и составных компонентов

Slim.js поддерживает слоты, позволяя создавать составные компоненты с гибкой вставкой контента:

Slim.tag('card-component', class extends Slim {
  get template() {
    return `
      <div class="card">
        <slot name="header"></slot>
        <slot></slot>
        <slot name="footer"></slot>
      </div>
    `;
  }
});

Использование:

<card-component>
  <h2 slot="header">Заголовок</h2>
  <p>Основной контент</p>
  <div slot="footer">Подвал</div>
</card-component>

Слоты обеспечивают гибкую композицию компонентов, что критично при построении сложных интерфейсов и micro-frontends.

Интеграция с micro-frontends

Micro-frontends предполагают разбиение интерфейса на независимые, изолированные части, которые могут разрабатываться и деплоиться отдельно. Slim.js идеально подходит для этой архитектуры благодаря лёгкости и изоляции компонентов:

  • Каждый компонент может быть отдельным micro-frontend, подключаемым через стандартные веб-компоненты.
  • Обновление одного компонента не требует перезагрузки всего приложения.
  • Использование слотов и событий облегчает коммуникацию между micro-frontends.

Пример взаимодействия двух micro-frontends:

<user-card id="user1" user-name="Иван"></user-card>
<user-actions for-user="user1"></user-actions>

Компонент user-actions может получать события от user-card через кастомные события, обеспечивая независимость модулей.

Динамическая загрузка и ленивый рендеринг

Для micro-frontends критично уменьшение нагрузки на основной bundle. Slim.js поддерживает динамическую загрузку компонентов:

async function loadComponent(name) {
  const module = await import(`./components/${name}.js`);
  return module;
}

loadComponent('user-card').then(() => {
  document.body.appendChild(document.createElement('user-card'));
});

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

Поддержка состояния и интеграция с внешними хранилищами

Slim.js можно использовать с внешними хранилищами состояния, например Redux или Zustand, благодаря реактивной привязке данных:

store.subscribe(() => {
  userCard.name = store.getState().userName;
});

Это позволяет синхронизировать глобальное состояние с локальными компонентами, что особенно важно при масштабных micro-frontends.

Стиль и изоляция

Slim.js не накладывает ограничений на стили, но рекомендуется использовать Shadow DOM для изоляции:

Slim.tag('shadow-card', class extends Slim {
  get useShadow() { return true; }

  get template() {
    return `<p>Контент с изолированными стилями</p>`;
  }
});

Изоляция стилей предотвращает конфликты между micro-frontends и упрощает поддержку крупного интерфейса.

Итоговые возможности

Slim.js сочетает:

  • лёгкость и минимальный размер,
  • реактивность данных,
  • полное управление жизненным циклом,
  • гибкую композицию через слоты,
  • поддержку Shadow DOM и интеграцию с micro-frontends.

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