Повторное использование логики

FAST Element — это современная библиотека для создания веб-компонентов с высокой производительностью и удобным API. Одной из ключевых задач при разработке на FAST Element является повторное использование логики без дублирования кода. Существуют несколько подходов, которые позволяют структурировать компоненты так, чтобы бизнес-логика была модульной, легко тестируемой и расширяемой.


Миксины (Mixins)

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

Пример:

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

function Focusable(Base) {
  return class extends Base {
    focus() {
      this.shadowRoot?.querySelector('input')?.focus();
    }
  };
}

class MyInput extends Focusable(FASTElement) {
  value = "";
}

MyInput.define({
  name: "my-input",
  template: html`<input type="text" :value="${x => x.value}">`,
  styles: css`input { border: 1px solid #ccc; padding: 4px; }`
});

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


Классы-утилиты

Для изоляции логики, не зависящей от DOM-компонента, удобно использовать отдельные классы-утилиты. Такой подход полезен для обработки данных, валидации, работы с API и других задач, которые могут использоваться в нескольких компонентах.

Пример:

export class Validator {
  static isEmail(value) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
  }

  static isNotEmpty(value) {
    return value.trim().length > 0;
  }
}

// Использование в компоненте
import { Validator } from './validator.js';

class EmailInput extends FASTElement {
  email = "";

  validate() {
    return Validator.isEmail(this.email);
  }
}

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


Повторное использование шаблонов

FAST Element поддерживает повторное использование шаблонов через template fragments и composition. Это позволяет создавать переиспользуемые блоки UI, не дублируя разметку.

Пример:

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

export const labelTemplate = html`
  <label :for="${x => x.for}">${x => x.text}</label>
`;

class TextInput extends FASTElement {
  for = "input1";
  text = "Введите текст:";
}

TextInput.define({
  name: "text-input",
  template: html`${labelTemplate}<input id="${x => x.for}" type="text">`
});

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


Контроллеры (Controllers)

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

Пример:

import { FASTElement, html, Observable, FASTElementController } from "@microsoft/fast-element";

class TooltipController {
  constructor(element) {
    this.element = element;
    this.show = false;
  }

  toggle() {
    this.show = !this.show;
    this.element.requestUpdate();
  }
}

class ButtonWithTooltip extends FASTElement {
  tooltipController;

  connectedCallback() {
    super.connectedCallback();
    this.tooltipController = new TooltipController(this);
  }
}

ButtonWithTooltip.define({
  name: "button-tooltip",
  template: html`
    <button @click="${x => x.tooltipController.toggle()}">Hover me</button>
    <div ?hidden="${x => !x.tooltipController.show}">Подсказка</div>
  `
});

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


Наследование компонентов

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

Пример:

class BaseInput extends FASTElement {
  value = "";
  
  handleChange(e) {
    this.value = e.target.value;
  }
}

class PasswordInput extends BaseInput {}

PasswordInput.define({
  name: "password-input",
  template: html`<input type="password" :value="${x => x.value}" @input="${x => x.handleChange($event)}">`
});

Ключевой момент: наследование упрощает повторное использование базовой функциональности, но может привести к жесткой связности между компонентами, поэтому рекомендуется комбинировать с миксинами и контроллерами.


Использование реактивных свойств и наблюдаемых коллекций

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

import { FASTElement, html, observable } from "@microsoft/fast-element";

class TodoList extends FASTElement {
  @observable items = [];

  addItem(item) {
    this.items = [...this.items, item];
  }

  removeItem(index) {
    this.items = this.items.filter((_, i) => i !== index);
  }
}

TodoList.define({
  name: "todo-list",
  template: html`
    <ul>
      ${x => x.items.map(item => html`<li>${item}</li>`)}
    </ul>
  `
});

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


Комбинирование подходов

На практике для больших проектов часто используется смешанный подход:

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

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


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