[Medium] ref vs reactive
1. Что такое ref и reactive?
Что такое
refиreactive?
ref и reactive — это два основных API в Vue 3 Composition API для создания реактивного состояния.
ref
Определение: ref создаёт реактивную обёртку для примитивного значения или ссылки на объект.
Нажмите, чтобы развернуть базовый пример ref
<script setup>
import { ref } from 'vue';
// примитивы
const count = ref(0);
const message = ref('Hello');
const isActive = ref(true);
// объект тоже работает с ref
const user = ref({
name: 'John',
age: 30,
});
// доступ через .value в JavaScript
console.log(count.value); // 0
count.value++;
</script>
reactive
Определение: reactive создаёт реактивный Proxy-объект (не для примитивных значений напрямую).
Нажмите, чтобы развернуть базовый пример reactive
<script setup>
import { reactive } from 'vue';
const state = reactive({
count: 0,
message: 'Hello',
user: {
name: 'John',
age: 30,
},
});
// прямой доступ к свойствам
console.log(state.count); // 0
state.count++;
</script>
2. ref vs reactive: ключевые различия
Основные различия между
refиreactive
1. Поддерживаемые т ипы
ref: работает с любым типом.
const count = ref(0); // number
const message = ref('Hello'); // string
const isActive = ref(true); // boolean
const user = ref({ name: 'John' }); // object
const items = ref([1, 2, 3]); // array
reactive: работает с объектами (включая массивы), не с примитивами.
const state = reactive({ count: 0 }); // object
const list = reactive([1, 2, 3]); // array
const count = reactive(0); // некорректное использование
const message = reactive('Hello'); // некорректное использование
2. Стиль доступа
ref: используйте .value в JavaScript.
Нажмите, чтобы развернуть пример доступа к ref
<script setup>
import { ref } from 'vue';
const count = ref(0);
console.log(count.value);
count.value = 10;
</script>
<template>
<div>{{ count }}</div>
<!-- автоматически разворачивается в шаблоне -->
</template>
reactive: прямой доступ к свойствам.
Нажмите, чтобы развернуть пример доступа к reactive
<script setup>
import { reactive } from 'vue';
const state = reactive({ count: 0 });
console.log(state.count);
state.count = 10;
</script>
<template>
<div>{{ state.count }}</div>
</template>
3. Поведение при переприсваивании
ref: можно переприсвоить.
const user = ref({ name: 'John' });
user.value = { name: 'Jane' }; // допустимо
reactive: не следует переприсваивать новую привязку объекта.
let state = reactive({ count: 0 });
state = { count: 10 }; // теряется реактивная связь
4. Деструктуризация
ref: деструктуризация ref.value даёт простые значения (не реактивные).
const user = ref({ name: 'John', age: 30 });
const { name, age } = user.value; // простые значения
reactive: прямая деструктуризация теряет реактивность.
const state = reactive({ count: 0, message: 'Hello' });
const { count, message } = state; // теряется реактивность
import { toRefs } from 'vue';
const refs = toRefs(state);
// refs.count и refs.message сохраняют реактивность
3. Когда использовать ref, а когда reactive?
Когда следует выбирать каждый API?
Используйте ref, когда
- Состояние является примитивом.
const count = ref(0);
const message = ref('Hello');
- Вам может понадобиться заменить всё значение/объект.
const user = ref({ name: 'John' });
user.value = { name: 'Jane' };
- Вам нужны template refs.
<template>
<div ref="container"></div>
</template>
<script setup>
const container = ref(null);
</script>
- Вы хотите единообразный стиль
.valueдля всех значений.
Используйте reactive, когда
- Управляете сложным состоянием объекта.
const formState = reactive({
username: '',
password: '',
errors: {},
});
- Группируете связанные поля вместе без замены идентичности объекта.
const userState = reactive({
user: null,
loading: false,
error: null,
});
- Предпочитаете прямой доступ к свойствам для вложенных структур.
4. Частые вопросы на собеседованиях
Частые вопросы на собеседованиях
Вопрос 1: базовые различия
Объясните вывод и поведение:
// случай 1: ref
const count1 = ref(0);
count1.value = 10;
console.log(count1.value); // ?
// случай 2: reactive
const state = reactive({ count: 0 });
state.count = 10;
console.log(state.count); // ?
// случай 3: переприсваивание reactive
let state2 = reactive({ count: 0 });
state2 = { count: 10 };
console.log(state2.count); // ?
Нажмите, чтобы увидеть ответ
console.log(count1.value); // 10
console.log(state.count); // 10
console.log(state2.count); // 10 (значение есть, но уже не реактивное)
Ключевые моменты:
refтребует.valuereactiveиспользует прямой доступ к свойствам- переприсваивание привязки
reactiveнарушает отслеживание реактивности
Вопрос 2: подводный камень деструктуризации
Что здесь не так и как это исправить?
// случай 1: деструктуризация ref
const user = ref({ name: 'John', age: 30 });
const { name, age } = user.value;
name = 'Jane'; // ?
// случай 2: деструктуризация reactive
const state = reactive({ count: 0, message: 'Hello' });
const { count, message } = state;
count = 10; // ?
Нажмите, чтобы увидеть ответ
Случай 1 (ref):
const user = ref({ name: 'John', age: 30 });
const { name, age } = user.value;
name = 'Jane'; // не обновляет user.value.name
// правильно
user.value.name = 'Jane';
// или
user.value = { name: 'Jane', age: 30 };
Случай 2 (reactive):
const state = reactive({ count: 0, message: 'Hello' });
const { count, message } = state;
count = 10; // теряется реактивность
// правильный подход 1
state.count = 10;
// правильный подход 2
import { toRefs } from 'vue';
const refs = toRefs(state);
refs.count.value = 10;
Резюме:
- деструктурированные простые значения не являются реактивными
- используйте
toRefsдля деструктуризации реактивного объекта
Вопрос 3: выбор ref или reactive
Выберите API для каждого сценария:
// Сценарий 1: счётчик
const count = ?;
// Сценарий 2: состояние формы
const form = ?;
// Сценарий 3: объект пользователя, который может быть заменён
const user = ?;
// Сценарий 4: template ref
const inputRef = ?;
Нажмите, чтобы увидеть ответ
const count = ref(0); // примитив
const form = reactive({
username: '',
password: '',
errors: {},
}); // сгруппированное состояние объекта
const user = ref({ name: 'John', age: 30 }); // удобнее для полной замены
const inputRef = ref(null); // template ref должен использовать ref
Правило:
- примитив ->
ref - нужна полная замена объекта ->
ref - сложное сгруппированное состояние объекта ->
reactive - template refs ->
ref
5. Лучшие практики
Лучшие практики
Рекомендуется
// 1) примитивы с ref
const count = ref(0);
const message = ref('Hello');
// 2) структурированное состояние объекта с reactive
const formState = reactive({
username: '',
password: '',
errors: {},
});
// 3) используйте ref, когда часто нужна полная замена
const user = ref({ name: 'John' });
user.value = { name: 'Jane' };
// 4) используйте toRefs при деструктуризации reactive объекта
import { toRefs } from 'vue';
const { username, password } = toRefs(formState);