Реактивность: ref, reactive, shallowRef, toRef, toRefs

↑ 4.1 Vue 3: основы, компоненты, Composition API · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Ядро Vue: как работает реактивность, когда ref, когда reactive, что такое .value.

Объяснение

Реактивность Vue 3 основана на Proxy: чтение внутри эффекта регистрирует зависимость (track), запись уведомляет подписчиков (trigger).

import { ref, reactive, toRef, toRefs, shallowRef, shallowReactive, readonly, isRef, unref, toRaw, triggerRef } from "vue";
 
const count = ref(0);                    // count.value
count.value++;
const user = reactive({ name: "A", address: { city: "M" } });   // прокси объекта, без .value
user.address.city = "SPb";                                        // глубокая реактивность
 
const state = reactive({ a: 1, b: 2 });
const a = toRef(state, "a");              // ref, связанный с полем (реактивность сохраняется)
const { a: a2, b } = toRefs(state);       // все поля как refs — безопасная деструктуризация
 
const big = shallowRef(hugeObject);       // реактивно только .value = ...; внутренности не отслеживаются
big.value = { ...big.value };             // замена триггерит; мутация — нет (triggerRef(big))
const ro = readonly(state);
APIНазначение
ref(x)контейнер для значения любого типа, .value
reactive(obj)глубокий реактивный прокси объекта (не примитивы)
shallowRef/shallowReactiveтолько верхний уровень (производительность больших структур)
toRef/toRefsсвязь с полями реактивного объекта
readonlyзащита от записи
toRaw, markRawдоступ к исходному объекту / отключение реактивности
isRef, unref, toValueвспомогательные
customRefсвоя логика (debounce ref)

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

В шаблоне ref верхнего уровня разворачивается автоматически ({{ count }}); внутри вложенных объектов — только если ref лежит в reactive.

Нюансы и подводные камни

  • ref внутри обычного объекта (не reactive) не разворачивается: нужен .value.
  • reactive нельзя полностью заменить: state = {...} теряет реактивность (используйте Object.assign или ref).
  • Прокси не идентичен исходному объекту: state !== toRaw(state).
  • Большие неизменяемые структуры (карты, графики, экземпляры классов) — shallowRef/markRaw.
  • Реактивность работает только для объектов, созданных до чтения; добавление новых свойств отслеживается (в отличие от Vue 2).

Практика

  1. Сравните поведение ref/reactive при деструктуризации и замене.
  2. Оберните большую структуру в shallowRef и обновляйте заменой.
  3. Реализуйте debouncedRef через customRef.

Вопросы с ответами

Связанные темы