Реактивность: ref и reactive
Что такое реактивность
Реактивность — Vue следит за данными и обновляет UI при изменениях.
Основа Vue. Без неё — обычный HTML.
ref — реактивное значение
ref() — обёртка для примитива или объекта.
import { ref } from 'vue';
const count = ref(0);
const name = ref('Иван');
const user = ref({ name: 'Иван', age: 25 });
const items = ref([]);
Доступ в JS:
count.value; // 0
count.value = 5;
name.value; // 'Иван'
user.value.name; // 'Иван'
user.value.age = 30;
items.value.push('item');
Доступ в шаблоне — без .value:
<p>{{ count }}</p>
<p>{{ name }}</p>
<p>{{ user.name }}</p>
Правило:
- В JS —
.value - В шаблоне — без
.value
reactive — реактивный объект
reactive() — для объектов и массивов.
import { reactive } from 'vue';
const user = reactive({
name: 'Иван',
age: 25,
address: {
city: 'Москва',
},
});
user.name; // 'Иван' — без .value
user.name = 'Петр';
user.address.city; // вложенные тоже реактивны
ref vs reactive
| ref | reactive | |
|---|---|---|
| Для | любых | объектов |
| Доступ | .value | напрямую |
| Переприсвоить | да | нет |
| Деструктуризация | да | теряет реактивность |
| В шаблоне | без .value | без .value |
Правило: используй ref — универсальнее.
Переприсвоение
ref:
const user = ref({ name: 'Иван' });
user.value = { name: 'Петр' }; // ✅ работает
reactive:
let user = reactive({ name: 'Иван' });
user = { name: 'Петр' }; // ❌ не работает — теряется реактивность
Деструктуризация
ref:
const user = ref({ name: 'Иван', age: 25 });
const { name, age } = user.value; // ❌ теряется реактивность
Правильно — toRefs:
import { toRefs } from 'vue';
const user = reactive({ name: 'Иван', age: 25 });
const { name, age } = toRefs(user); // ✅ сохраняется
name.value = 'Петр';
Или toRef:
import { toRef } from 'vue';
const name = toRef(user, 'name');
Вложенная реактивность
ref:
const user = ref({
name: 'Иван',
address: {
city: 'Москва',
},
});
user.value.address.city = 'СПб'; // ✅ работает
reactive:
const user = reactive({
name: 'Иван',
address: { city: 'Москва' },
});
user.address.city = 'СПб'; // ✅ работает
Оба реактивны на любую глубину.
Реактивность в массивах
const items = ref(['a', 'b']);
items.value.push('c'); // ✅
items.value[0] = 'x'; // ✅
items.value = ['new']; // ✅
items.value.length; // реактивно
const items = reactive(['a', 'b']);
items.push('c'); // ✅
items[0] = 'x'; // ✅
items = ['new']; // ❌ потеря реактивности
ref в шаблоне
<script setup>
import { ref } from 'vue';
const count = ref(0);
const user = ref({ name: 'Иван' });
</script>
<template>
<p>{{ count }}</p> <!-- без .value -->
<p>{{ user.name }}</p> <!-- Vue сам разворачивает -->
</template>
Vue автоматически разворачивает .value в шаблоне.
ref в объектах
const form = ref({
name: ref(''),
email: ref(''),
});
form.value.name = 'Иван'; // двойной .value — не работает
Правильно:
const form = reactive({
name: '',
email: '',
});
shallowRef — поверхностная реактивность
Только верхний уровень:
import { shallowRef } from 'vue';
const user = shallowRef({ name: 'Иван', age: 25 });
user.value.name = 'Петр'; // ❌ не триггерит обновление
user.value = { name: 'Петр' }; // ✅
Зачем: производительность для больших структур.
triggerRef — принудительное обновление
import { shallowRef, triggerRef } from 'vue';
const user = shallowRef({ name: 'Иван' });
user.value.name = 'Петр';
triggerRef(user); // принудительно обновить
Практика
<script setup>
import { ref, reactive, toRefs } from 'vue';
// Примитив
const count = ref(0);
// Объект через ref
const user = ref({
name: 'Иван',
age: 25,
});
// Объект через reactive
const settings = reactive({
theme: 'dark',
language: 'ru',
});
// toRefs для деструктуризации
const { theme, language } = toRefs(settings);
function increment() {
count.value++;
}
function updateUser() {
user.value.name = 'Петр';
}
function changeTheme() {
settings.theme = settings.theme === 'dark' ? 'light' : 'dark';
}
</script>
<template>
<p>Счётчик: {{ count }}</p>
<button @click="increment">+1</button>
<p>Пользователь: {{ user.name }}, {{ user.age }}</p>
<button @click="updateUser">Изменить имя</button>
<p>Тема: {{ theme }}</p>
<button @click="changeTheme">Переключить</button>
</template>
Частые ошибки
1. Забыл .value в JS:
const count = ref(0);
count++; // ❌ не работает
count.value++; // ✅
2. Деструктуризация reactive:
const { name } = user; // ❌ теряет реактивность
3. Переприсвоение reactive:
user = {}; // ❌ потеря
user.name = ''; // ✅
4. .value в шаблоне:
{{ count.value }} <!-- лишнее -->
Итог
- ref — универсально, с
.valueв JS - reactive — для объектов, без
.value - toRefs — деструктуризация с сохранением реактивности
- shallowRef — только верхний уровень
- Правило: для всего —
ref
Дальше — toRefs и toRef.