Декоратор observable

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

observable превращает обычное поле класса в реактивный источник данных. При изменении значения поле генерирует уведомление, позволяя системе шаблонов FAST обновить DOM без ручных вызовов рендера.

Ключевые особенности:

  • Определение «наблюдаемого» свойства на прототипе класса.
  • Генерация уведомлений об изменении значения.
  • Двустороннее связывание с шаблонами FAST.
  • Опциональные преобразователи и правила сравнения.

Определение свойства через observable

import { observable } from '@microsoft/fast-element';

class Counter {
    @observable count = 0;
}

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

Кастомизация поведения

Декоратор принимает опции для тонкой настройки. Наиболее часто используются change и valueConverter.

class User {
    @observable({
        change: (source, value) => console.log('changed:', value)
    })
    name = '';
}

Опция change перехватывает событие изменения. Это полезно для валидации, логирования и побочных действий.

class Person {
    @observable({
        valueConverter: {
            toView: value => value.toUpperCase(),
            fromView: value => value.trim()
        }
    })
    city = '';
}

valueConverter используется в двустороннем связывании и обеспечивает преобразование данных для отображения и обратно.

Реактивность и шаблоны

FAST использует механизм наблюдения при связывании свойств с шаблоном. Синтаксис .property=${expr} и :property=${expr} связывает выражение с реактивным обновлением. Изменение наблюдаемого свойства вызывает перебор связей и обновление DOM.

html<Counter>`
    <span>${x => x.count}</span>
    <button @click=${x => x.count++}>+</button>
`;

При увеличении count значение внутри span меняется без дополнительного кода.

Тонкости сравнения значений

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

@observable({
    change: (source, value, oldValue) => {
        if (value === oldValue) return;
        // дальнейшая обработка
    }
})
level = 0;

Наблюдение за сложными структурами

observable отслеживает только присвоение свойства, а не изменения внутри сложных структур. Для массивов и объектов используется отдельный механизм Observable из FAST Core. Это важное различие: реактивность не является глубокой по умолчанию.

@observable items = [];
// items.push(…) не вызовет обновления

При необходимости создаются обертки для массивов через Observable.getArrayObserver.

Интеграция с жизненным циклом компонента

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

Механизм нотификаций

При присвоении значения FAST выполняет цепочку действий:

  1. Чтение текущего значения.
  2. Сравнение с новым.
  3. Установка нового значения.
  4. Генерация уведомления.
  5. Обновление шаблона или запуск кастомных обработчиков.

Эта цепочка делает реактивность детерминированной и предсказуемой.

Взаимодействие с атрибутами

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

@attr
@observable title;

Изменение атрибута приводит к обновлению свойства и, следовательно, шаблона.

Сочетание с computed-полями

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

@observable first = '';
@observable last = '';

get full() {
    return `${this.first} ${this.last}`;
}

Такой подход упрощает композицию состояния без скрытой магии.

Производительность

observable минимизирует накладные расходы. Уведомления происходят только при присвоении, что снижает цену реактивности по сравнению с прокси- или diff-базированными системами. Для сложных интерфейсов это обеспечивает линейное масштабирование количества обновлений.

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

Рекомендации использования

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

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