Создание пользовательских директив

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

Директива в FAST Element — это функция, возвращающая объект с методами жизненного цикла: bind, update и unbind. Она применяется внутри шаблона с помощью синтаксиса ${directive()}.


Структура пользовательской директивы

Типичная директива состоит из нескольких ключевых частей:

  1. Функция директивы Создает замыкание для хранения состояния и конфигурации.

  2. Метод bind Вызывается при первом связывании с DOM. Используется для инициализации элементов и установки первоначальных значений.

  3. Метод update Отвечает за обновление состояния при изменении данных. Позволяет изменять DOM без полной перерисовки.

  4. Метод unbind Очищает ресурсы, удаляет слушатели и отменяет побочные эффекты при удалении элемента из DOM.

Пример минимальной структуры:

import { directive, NodePart } from '@microsoft/fast-element';

export const myDirective = directive((value) => {
    return {
        bind(part) {
            if (part instanceof NodePart) {
                part.setValue(`Initial: ${value}`);
            }
        },
        update(part, [value]) {
            if (part instanceof NodePart) {
                part.setValue(`Updated: ${value}`);
            }
        },
        unbind(part) {
            // очистка ресурсов
        }
    };
});

Применение директив в шаблонах

Директива подключается в шаблоне компонента следующим образом:

import { html, FASTElement } from '@microsoft/fast-element';
import { myDirective } from './directives/myDirective.js';

const template = html<MyComponent>`
    <div>${x => myDirective(x.someValue)}</div>
`;

class MyComponent extends FASTElement {
    someValue = 'Тест';
}

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


Работа с атрибутами и событиями

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

bind(part) {
    if (part.type === 'attribute') {
        part.setValue(`color: ${this.value}`);
    } else if (part.type === 'node') {
        part.setValue(this.value);
    }
}

Для добавления обработчиков событий:

update(part, [handler]) {
    if (part instanceof NodePart) {
        const node = part.start.parentNode;
        node.removeEventListener('click', part.oldHandler);
        node.addEventListener('click', handler);
        part.oldHandler = handler;
    }
}

Параметры и реактивность

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

export const reactiveDirective = directive((value) => {
    let previousValue;
    return {
        update(part) {
            if (previousValue !== value) {
                part.setValue(`Value changed to: ${value}`);
                previousValue = value;
            }
        }
    };
});

Таким образом, перерисовка происходит только при реальном изменении данных, что повышает производительность.


Сложные директивы и композиция

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

export const compositeDirective = directive((value) => {
    const inner = anotherDirective(value);
    return {
        bind(part) {
            inner.bind(part);
        },
        update(part, [value]) {
            inner.update(part, [value]);
        }
    };
});

Это позволяет строить модульные и повторно используемые решения без дублирования кода.


Особенности производительности

  • Минимизация изменений DOM: директива обновляет только ту часть шаблона, которая изменилась.
  • Кэширование состояний: замыкания позволяют хранить состояние между обновлениями, что исключает лишние операции.
  • Поддержка всех типов частей шаблона: узлы, атрибуты, boolean-атрибуты, события.

Практические рекомендации

  • Для простых операций достаточно метода update.
  • Для работы с DOM-узлами и сторонними библиотеками лучше использовать bind и unbind.
  • Хранение локального состояния директивы повышает контроль над изменениями и предотвращает утечки памяти.
  • Композиция директив упрощает поддержку и тестирование сложных реактивных шаблонов.

Пользовательские директивы в FAST Element предоставляют мощный инструмент для управления DOM и реактивностью, позволяя создавать высокопроизводительные, читаемые и модульные компоненты.