> Как решить проблему потери реактивности при деструктуризации пропсов во Vue? (Vue.js)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: The Tribe

Стек: Vue.js

> Пример ответа

Короткий ответ

Проблема потери реактивности при деструктуризации пропсов во Vue возникает, потому что деструктуризация создаёт новые локальные переменные, которые не являются реактивными ссылками на исходные пропсы. Решение - использовать toRefs для преобразования пропсов в набор ref-объектов, либо обращаться к пропсам через объект props без деструктуризации. В Composition API также можно применить toRef для отдельных полей.

Подробное объяснение

Во Vue реактивность пропсов обеспечивается через объект props, который является реактивным. При деструктуризации:

JAVASCRIPT
const { name, age } = props; // name и age - обычные значения, не реактивные

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

Правильные подходы:

  1. Использовать toRefs(props) - создаёт ref-объекты для каждого поля, сохраняя реактивность
  2. Использовать toRef(props, 'fieldName') - для отдельных полей
  3. Обращаться напрямую через props.fieldName в шаблоне или computed

В Options API проблема менее заметна, так как this.propName автоматически обращается к реактивному объекту this.$props, но при явной деструктуризации в методах или computed она возникает.

На практике

В реальных проектах чаще всего используют toRefs в setup-функции или <script setup>. Это стандартный паттерн для работы с пропсами в Composition API. Важно помнить, что toRefs создаёт ref-объекты, которые синхронизируются с исходными пропсами - при изменении пропса обновляется ref, и наоборот.

Для глубоких объектов (массивов, вложенных структур) toRefs работает корректно, так как создаёт ref на весь объект, а не на его копию.

Пример кода

<script setup>
import { toRefs, toRef } from 'vue'

// Проблемный вариант (потеря реактивности)
const { name, age } = props
watch(name, () => {}) // не сработает при изменении пропса

// Правильный вариант 1 - toRefs
const { name, age } = toRefs(props)
watch(name, (newVal) => {
  console.log('name changed:', newVal)
})

// Правильный вариант 2 - toRef для одного поля
const nameRef = toRef(props, 'name')

// Правильный вариант 3 - без деструктуризации
watch(() => props.name, (newVal) => {
  console.log('name changed:', newVal)
})
</script>

Как отвечать на собеседовании

Начни с объяснения причины - деструктуризация создаёт копии значений, а не реактивные ссылки. Затем перечисли решения: toRefs, toRef, прямой доступ через props. Подчеркни, что это особенность именно Composition API, в Options API проблема проявляется реже. Приведи пример с watch или computed, где потеря реактивности критична. Упомяни, что для примитивов проблема очевидна, а для объектов нужно быть внимательным с вложенными изменениями.

Что проверяет интервьюер

  • Понимание механизма реактивности Vue (Proxy, getter/setter)
  • Знание различий между Options API и Composition API
  • Умение работать с пропсами и их реактивностью
  • Понимание, когда деструктуризация безопасна (например, в шаблоне через v-bind)
  • Знание утилит Vue: toRefs, toRef, reactive, ref

Типичные ошибки

  • Использование reactive(props) вместо toRefs - это создаёт новый реактивный объект, не связанный с исходными пропсами
  • Деструктуризация в шаблоне через v-bind="{ name, age }" - безопасно, так как это передача значений, а не потеря реактивности
  • Забывают, что toRefs работает только для объектов, а не для примитивов напрямую
  • Путают toRefs с toRef - первый преобразует весь объект, второй - одно поле
  • Пытаются использовать деструктуризацию в computed или watch без оборачивания в функцию

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью