toRefs и toRef
Проблема деструктуризации
reactive удобен, но при деструктуризации теряет реактивность:
import { reactive } from 'vue';
const user = reactive({
name: 'Иван',
age: 25,
});
const { name } = user; // обычная строка, не реактивна
name; // 'Иван'
user.name = 'Петр'; // реактивность есть
console.log(name); // всё ещё 'Иван'
Что не так: name — копия значения, не ссылка.
toRefs — решение
Преобразует reactive в объект с ref-ами:
import { reactive, toRefs } from 'vue';
const user = reactive({
name: 'Иван',
age: 25,
});
const { name, age } = toRefs(user);
name.value; // 'Иван'
name.value = 'Петр'; // меняет user.name
user.name; // 'Петр'
Каждое поле становится ref, связанным с user.
Когда использовать
1. Возврат из composable:
import { reactive, toRefs } from 'vue';
export function useUser() {
const state = reactive({
name: '',
email: '',
loading: false,
});
function load() {
state.loading = true;
}
return { ...toRefs(state), load };
}
Использование:
<script setup>
import { useUser } from './composables/useUser';
const { name, email, loading, load } = useUser();
</script>
<template>
<p v-if="loading">Загрузка...</p>
<p>{{ name }} — {{ email }}</p>
</template>
2. Прокидывание в props:
const { title, count } = toRefs(state);
Теперь можно передать через v-model или как ref.
toRef — один ref
toRef — превращает одно поле reactive в ref.
import { reactive, toRef } from 'vue';
const user = reactive({ name: 'Иван', age: 25 });
const name = toRef(user, 'name');
name.value; // 'Иван'
name.value = 'Петр';
user.name; // 'Петр'
toRef для props
const props = defineProps({ title: String });
const title = toRef(props, 'title');
Зачем: можно передавать в composable.
toRef с дефолтом
const title = toRef(props, 'title', 'Default');
3-й аргумент — значение по умолчанию.
toRefs vs toRef
| toRefs | toRef | |
|---|---|---|
| Для | всех полей | одного поля |
| Результат | объект с ref-ами | один ref |
| Reactive-источник | да | да |
| Props | нет | да |
Практика — форма
<script setup>
import { reactive, toRefs } from 'vue';
const form = reactive({
name: '',
email: '',
message: '',
agree: false,
});
const { name, email, message, agree } = toRefs(form);
function submit() {
console.log('Отправлено:', form);
}
function reset() {
form.name = '';
form.email = '';
form.message = '';
form.agree = false;
}
</script>
<template>
<form @submit.prevent="submit">
<input v-model="name" placeholder="Имя">
<input v-model="email" type="email" placeholder="Email">
<textarea v-model="message" placeholder="Сообщение"></textarea>
<label>
<input type="checkbox" v-model="agree">
Согласен
</label>
<button :disabled="!agree">Отправить</button>
<button type="button" @click="reset">Сброс</button>
</form>
<pre>{{ form }}</pre>
</template>
v-model работает с name.value через v-model="name" — Vue сам разворачивает.
Composable с toRefs
import { ref, toRefs } from 'vue';
export function useCounter(initial = 0) {
const state = ref({
count: initial,
history: [],
});
function increment() {
state.value.count++;
state.value.history.push(state.value.count);
}
function reset() {
state.value.count = initial;
state.value.history = [];
}
return {
...toRefs(state.value),
increment,
reset,
};
}
Использование:
<script setup>
import { useCounter } from '@/composables/useCounter';
const { count, history, increment, reset } = useCounter(10);
</script>
<template>
<p>Count: {{ count }}</p>
<p>History: {{ history }}</p>
<button @click="increment">+1</button>
<button @click="reset">Reset</button>
</template>
Работа с массивами
const state = reactive({ items: [1, 2, 3] });
const { items } = toRefs(state);
items.value.push(4); // работает
items.value = [5]; // работает
toRef на computed
const fullName = computed(() => `${first.value} ${last.value}`);
const nameRef = toRef({ fullName }, 'fullName');
Редко нужно.
В связке с props
<script setup>
import { toRef, watch } from 'vue';
const props = defineProps({
modelValue: String,
});
const value = toRef(props, 'modelValue');
watch(value, (val) => console.log('Changed:', val));
</script>
value — реактивная ссылка на prop.
Итог
toRefs(reactive)— объект с ref-ами, сохраняет реактивностьtoRef(obj, key)— один ref- Используй toRefs при возврате из composable
- toRef для props — чтобы следить за изменениями
Дальше — provide и inject.