Вложенные компоненты

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

Создание вложенных компонентов

В Slim.js каждый компонент создаётся через класс, наследующийся от Slim. Вложенные компоненты можно подключать к родительскому через обычный HTML-тег, соответствующий имени зарегистрированного компонента.

Пример создания простого дочернего компонента:

import { Slim } from 'slim-js';

class ChildComponent extends Slim {
  get template() {
    return `<p>Дочерний компонент: {{message}}</p>`;
  }

  message = "Привет из ChildComponent";
}

customElements.define('child-component', ChildComponent);

Родительский компонент может использовать этот компонент следующим образом:

class ParentComponent extends Slim {
  get template() {
    return `
      <div>
        <h2>Родительский компонент</h2>
        <child-component></child-component>
      </div>
    `;
  }
}

customElements.define('parent-component', ParentComponent);

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

Передача данных в дочерние компоненты

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

class ChildComponent extends Slim {
  get template() {
    return `<p>Привет, {{name}}!</p>`;
  }

  name = "";
}

customElements.define('child-component', ChildComponent);

class ParentComponent extends Slim {
  get template() {
    return `
      <child-component name="{{userName}}"></child-component>
    `;
  }

  userName = "Алексей";
}

customElements.define('parent-component', ParentComponent);

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

Вложенные циклы компонентов

Вложенные компоненты особенно полезны при работе с коллекциями данных. Slim.js поддерживает динамическое создание компонентов через директиву {{#each}}:

class ItemComponent extends Slim {
  get template() {
    return `<li>{{item}}</li>`;
  }

  item = "";
}

customElements.define('item-component', ItemComponent);

class ListComponent extends Slim {
  get template() {
    return `
      <ul>
        {{#each items}}
          <item-component item="{{this}}"></item-component>
        {{/each}}
      </ul>
    `;
  }

  items = ["Яблоко", "Банан", "Вишня"];
}

customElements.define('list-component', ListComponent);

В данном примере каждый элемент массива items рендерится через отдельный экземпляр ItemComponent, сохраняя полную реактивность.

Обработка событий в вложенных компонентах

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

class ChildComponent extends Slim {
  get template() {
    return `<button oncl ick="{{handleClick}}">Нажми меня</button>`;
  }

  handleClick() {
    this.dispatchEvent(new CustomEvent('child-click', { detail: 'Данные от ребенка' }));
  }
}

customElements.define('child-component', ChildComponent);

class ParentComponent extends Slim {
  get template() {
    return `
      <child-component onchild-click="{{onChildClick}}"></child-component>
    `;
  }

  onChildClick(event) {
    console.log(event.detail); // Выведет: "Данные от ребенка"
  }
}

customElements.define('parent-component', ParentComponent);

Особенность Slim.js: привязка событий через on<event> работает напрямую с методами родителя, что упрощает коммуникацию между уровнями компонентов.

Управление жизненным циклом вложенных компонентов

Каждый компонент Slim.js имеет стандартные хуки жизненного цикла:

  • onBeforeCreated() — вызывается до создания DOM.
  • onAfterCreated() — вызывается после вставки в DOM.
  • onBeforeUpdate() и onAfterUpdate() — вызываются при изменении реактивных данных.
  • onBeforeRemoved() — перед удалением компонента из DOM.

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

class ChildComponent extends Slim {
  get template() {
    return `<p>{{message}}</p>`;
  }

  message = "Инициализация...";

  onAfterCreated() {
    setTimeout(() => {
      this.message = "Данные обновлены";
    }, 1000);
  }
}

customElements.define('child-component', ChildComponent);

Рекурсивные компоненты

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

class TreeNode extends Slim {
  get template() {
    return `
      <div>
        <span>{{name}}</span>
        {{#if children}}
          <ul>
            {{#each children}}
              <tree-node name="{{this.name}}" children="{{this.children}}"></tree-node>
            {{/each}}
          </ul>
        {{/if}}
      </div>
    `;
  }

  name = "";
  children = [];
}

customElements.define('tree-node', TreeNode);

Такой подход позволяет строить глубоко вложенные интерфейсы с полной реактивностью каждого узла.

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

  • Использовать {{#each}} вместо ручного создания множества компонентов через JavaScript.
  • Ограничивать обновления дочерних компонентов только необходимыми реактивными свойствами.
  • Вложенные компоненты следует проектировать как самодостаточные, чтобы минимизировать зависимость от состояния родителя.

В результате Slim.js позволяет строить сложные интерфейсы с минимальной связностью компонентов, обеспечивая модульность и простоту сопровождения кода.