Vue: Синтетический триггер реактивности «ручной пинок»

Продолжение исследований "странностей" Vue.

Схема «шедевра»: Представьте есть тяжелый computed-вычисляемый метод или глобальный стор, который упорно отказывается обновляться тогда, когда этого хочет разработчик (обычно из-за того, что данные приходят нереактивно, либо используются сторонние библиотеки). Вместо того чтобы разобраться с первопричиной, в коде рождается это:

``` const reactivityTrigger = ref(0) // Создается искусственный счетчик const myData = computed(() => {   // Подмешиваем переменную, просто чтобы Vue затрекал зависимость   const _ = reactivityTrigger.value;

return heavyDataCrunching(); // тяжелые вычисления }); ```

Где-то в недрах приложения создается шина событий (Event Bus). В нужный момент через шину прокидывается событие, поймав которое, обработчик делает reactivityTrigger.value++. Вуаля! Vue видит, что зависимость изменилась, и пересчитывает computed.

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

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

Как надо: Если вы столкнулись с ситуацией, когда реактивность хочется «пнуть» вручную, в 99% случаев вы воюете со следствием, а не с причиной. - Сделайте источник данных реактивным. Вместо неявного вызова функции heavyDataCrunching(), которая берет данные из ниоткуда, передавайте в computed конкретные ref или reactive свойства в качестве явных зависимостей.

- Используйте shallowRef и triggerRef. Если у вас есть огромный объект из внешней библиотеки, который нельзя делать глубоко реактивным ради перформанса, сделайте его поверхностно реактивным. А если его нужно принудительно обновить, во Vue для этого есть законный инструмент:

``` import { shallowRef, triggerRef } from 'vue';

// Vue следит ТОЛЬКО за изменением ссылки .value const chartData = shallowRef({ state: { someCount: 0 } });

// Изменение вложенного свойства НЕ вызовет перерендер: chartData.value.state.someCount = 99;

// Когда нужно принудительно обновить все зависимости: triggerRef(chartData); ```