Что такое reactive и ref во Vue Composition API, для чего они нужны и чем отличаются?
Что проверяют
Понимаете ли вы, как устроена реактивность Vue 3 на прокси, и знаете ли ограничения reactive, из-за которых данные перестают обновляться. Следом обычно спрашивают, почему после деструктуризации шаблон перестал реагировать и что выбирать по умолчанию.
Как отвечать
Обе функции делают данные реактивными: Vue отслеживает чтение внутри рендера, computed и watch и перезапускает их при изменении.
reactive(obj): возвращаетProxyнад объектом, массивом,MapилиSet. Реактивность глубокая, к полям обращаются напрямую:state.count++. Примитив передать нельзя;ref(value): оборачивает любое значение в объект с полем.value. Для примитивов это единственный путь, но и объект положить можно: он станет глубоко реактивным через тот жеreactiveвнутри;- распаковка: в шаблоне ref верхнего уровня разворачивается сам,
.valueне нужен. Ref внутриreactive-объекта тоже разворачивается, а внутри массива илиMapнет.
Главное отличие в ограничениях reactive. Реактивность привязана к прокси, а не к переменной, поэтому:
- заменить объект целиком нельзя:
state = reactive({...})рвёт связь с шаблоном, аlist.value = newListу ref работает; - деструктуризация теряет реактивность:
const { count } = stateдаёт обычное число, спасаетtoRefs(state); - передача поля в функцию: уходит значение, а не связь.
Поэтому документация Vue рекомендует ref как основной инструмент, а reactive оставляет для группы полей, которые живут вместе, например состояния формы. Для больших данных, которые только заменяются целиком, есть shallowRef. В Vue 3.5 деструктуризация defineProps в <script setup> сохраняет реактивность, но это работа компилятора, а не поведение reactive.
Где спотыкаются
Говорят, что ref только для примитивов. Он работает с любым значением и чаще всего предпочтительнее.
Передают в watch поле реактивного объекта: watch(state.count, cb) получает число, а не источник, и ничего не отслеживает. Нужен геттер () => state.count.
Путают прокси с исходным объектом: reactive(raw) === raw даёт false, а изменения через raw напрямую не вызывают обновления.