Механизм реактивности

Mind.js реализует реактивную архитектуру, где изменение состояния данных автоматически отражается на связанных компонентах. Механизм реактивности построен на наблюдателях и подписках, что позволяет минимизировать ручное управление DOM и упрощает синхронизацию данных.

Реактивные свойства создаются с помощью функции reactive(). Она принимает объект и возвращает его обёртку, которая отслеживает все чтения и записи:

import { reactive } from 'mind.js';

const state = reactive({
    count: 0,
    user: {
        name: 'Alice',
        age: 25
    }
});

Любое изменение свойства state.count или state.user.age автоматически уведомляет подписчиков, что позволяет компонентам перерисовываться при изменении данных.


Наблюдатели и подписки

Функция watch используется для отслеживания изменений конкретного свойства. Она принимает два аргумента: реактивное свойство и функцию-обработчик:

import { watch } from 'mind.js';

watch(() => state.count, (newValue, oldValue) => {
    console.log(`Count изменился с ${oldValue} на ${newValue}`);
});

Ключевые моменты работы watch:

  • Первый аргумент — функция, возвращающая наблюдаемое значение.
  • Второй аргумент — функция-обработчик, вызываемая при изменении значения.
  • Поддерживаются глубокие наблюдения за вложенными объектами.

Компьютируемые свойства

computed создаёт свойства, значение которых автоматически пересчитывается при изменении зависимостей. Это аналог производных данных:

import { computed } from 'mind.js';

const doubleCount = computed(() => state.count * 2);

Особенности computed:

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

Эффекты и реактивные функции

effect запускает функцию сразу и повторно при изменении зависимостей. Отличие от computed в том, что effect предназначен для побочных действий, а не для вычисления значения:

import { effect } from 'mind.js';

effect(() => {
    console.log(`Текущее значение count: ${state.count}`);
});

Особенности:

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

Глубокая реактивность и вложенные объекты

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

Пример:

state.user.name = 'Bob';  // триггерит эффекты, зависящие от state.user.name
state.user.age++;          // триггерит эффекты, зависящие от state.user.age

Для массивов реактивность распространяется на методы изменения:

state.items = reactive([]);
state.items.push('новый элемент'); // триггерит подписки

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

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

Рекомендации по оптимизации:

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

Примеры комплексного использования

Создание интерактивного счётчика с отображением производных значений:

const state = reactive({ count: 0 });

const double = computed(() => state.count * 2);

effect(() => {
    console.log(`Count: ${state.count}, Double: ${double.value}`);
});

state.count++; // Count: 1, Double: 2
state.count++; // Count: 2, Double: 4

Здесь видно:

  • Свойство double пересчитывается автоматически при изменении state.count.
  • Эффект срабатывает при каждом изменении зависимостей.
  • Нет необходимости вручную вызывать обновление.

Реактивные массивы и методы работы с ними

Mind.js предоставляет полную поддержку массивов:

const items = reactive(['apple', 'banana']);

watch(() => items.length, (newLength) => {
    console.log(`Количество элементов: ${newLength}`);
});

items.push('cherry'); // триггерит watch
items.splice(1, 1);   // удаляет 'banana' и триггерит watch

Особенности:

  • Методы push, pop, splice, shift, unshift и сортировка автоматически вызывают обновления.
  • Наблюдение за массивом работает как для длины, так и для конкретных индексов.
  • Можно комбинировать с computed для фильтрации или сортировки в реактивном виде.