Композиция поведений

FAST Element предоставляет мощный механизм для организации логики компонентов через поведения (behaviors). Поведения позволяют отделять функциональные аспекты компонента от его структуры и шаблона, делая код модульным, повторно используемым и более управляемым.

Основные понятия

Поведение — это объект, содержащий методы, свойства и реакции на события, которые могут быть привязаны к элементу. Оно не создаёт DOM само по себе, но расширяет возможности компонента, добавляя интерактивность или дополнительную логику.

Композиция поведений — это процесс объединения нескольких поведений в один компонент, так чтобы они работали совместно, не конфликтуя между собой. FAST Element поддерживает композицию на нескольких уровнях: декларативно через массив поведений или программно через вызовы API.

Создание поведения

Поведение определяется через объект с методами жизненного цикла и свойствами. Основные поля:

  • connectedCallback: вызывается при добавлении элемента в DOM.
  • disconnectedCallback: вызывается при удалении элемента из DOM.
  • attributeChangedCallback: реагирует на изменение атрибутов.
  • observedAttributes: список атрибутов, за которыми наблюдает поведение.
  • methods и properties: набор методов и реактивных свойств.

Пример простого поведения:

import { Behavior } from "@microsoft/fast-element";

export const FocusBehavior = Behavior({
    connectedCallback(element) {
        element.addEventListener('focus', this.handleFocus);
    },
    disconnectedCallback(element) {
        element.removeEventListener('focus', this.handleFocus);
    },
    methods: {
        handleFocus(event) {
            console.log('Элемент получил фокус', event.target);
        }
    }
});

Применение нескольких поведений

FAST Element позволяет объединять поведения через массив. Все методы жизненного цикла вызываются последовательно, что обеспечивает согласованное поведение.

import { FASTElement, html, css } from "@microsoft/fast-element";
import { FocusBehavior } from "./FocusBehavior.js";
import { HoverBehavior } from "./HoverBehavior.js";

const template = html`
    <div class="box">Hover или Focus сюда</div>
`;

const styles = css`
    .box { width: 200px; height: 100px; border: 1px solid black; }
`;

export class InteractiveBox extends FASTElement {
    static template = template;
    static styles = styles;
    static behaviors = [FocusBehavior, HoverBehavior];
}

В этом примере InteractiveBox одновременно использует логику фокуса и наведения. Каждый connectedCallback вызывается в порядке следования в массиве behaviors.

Конфликты и их разрешение

При композиции нескольких поведений могут возникнуть конфликты:

  • Совпадение имён методов или свойств.
  • Одновременные обработчики одного события, вызывающие нежелательные побочные эффекты.

Рекомендации по разрешению конфликтов:

  1. Явное разделение областей ответственности поведения: одно поведение — один аспект.
  2. Использование уникальных имён для методов и свойств.
  3. Возможность делегирования событий через dispatchEvent вместо прямого вмешательства в DOM.
  4. В случае дублирующихся методов жизненного цикла — использовать композицию через вызовы внутри метода:
connectedCallback(element) {
    FirstBehavior.connectedCallback.call(this, element);
    SecondBehavior.connectedCallback.call(this, element);
}

Параметризуемые поведения

Поведения могут быть настраиваемыми через параметры, что повышает их повторное использование. Параметры передаются при подключении поведения к элементу:

export const TooltipBehavior = (text) => Behavior({
    connectedCallback(element) {
        element.title = text;
    }
});

export class ButtonWithTooltip extends FASTElement {
    static template = html`<button>Наведите на меня</button>`;
    static behaviors = [TooltipBehavior("Подсказка")];
}

Реактивные свойства в поведениях

FAST Element поддерживает реактивность внутри поведения через properties. Это позволяет поведенческим объектам отслеживать изменения состояния и обновлять DOM компонента:

export const ToggleBehavior = Behavior({
    properties: {
        active: { type: Boolean, reflect: true }
    },
    methods: {
        toggle() {
            this.active = !this.active;
        }
    }
});

active автоматически обновляется в DOM, если связана с атрибутом или стилем через шаблон FAST.

Преимущества композиции поведений

  • Модульность: одна логика = одно поведение.
  • Повторное использование: одно поведение можно применять к разным элементам.
  • Поддержка SOLID-принципов: разделение обязанностей между компонентами и поведениями.
  • Упрощённое тестирование: поведения можно тестировать отдельно от компонентов.

Итоговая структура

Идеальная структура компонента с поведениями выглядит так:

  1. Шаблон и стили — декларативная часть.
  2. Логика компонентов — свойства и методы, специфичные для элемента.
  3. Подключаемые поведения — набор аспектов функциональности, вынесенных в отдельные модули.

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