Ці утиліти розв'язують одну проблему: як передати реактивне значення далі, не втративши реактивності.
toRefs(reactiveObj) - перетворює кожну властивість reactive-об'єкта на ref, пов'язаний з оригіналом. Головне застосування - повернути стан з composable так, щоб його можна було деструктуризувати:
function useMouse() {
const state = reactive({ x: 0, y: 0 });
// ...
return toRefs(state);
}
const { x, y } = useMouse(); // x і y - ref-и, реактивні
toRef - один ref:
const title = toRef(props, 'title'); // ref, що читає props.title
const fullName = toRef(() => `${props.first} ${props.last}`); // ref тільки для читання з геттера
toValue(source) (3.3+) - «розгорнути що завгодно»: ref - .value, геттер - результат виклику, звичайне значення - як є.
Composable, що приймає ref, геттер або значення - тип MaybeRefOrGetter:
import { ref, watchEffect, toValue, type MaybeRefOrGetter } from 'vue';
export function useFetch<T>(url: MaybeRefOrGetter<string>) {
const data = ref<T | null>(null);
watchEffect(async () => {
data.value = null;
const response = await fetch(toValue(url)); // читання всередині ефекту = залежність
data.value = await response.json();
});
return { data };
}
useFetch('/api/users'); // рядок
useFetch(userUrl); // ref
useFetch(() => `/api/users/${props.id}`); // геттер від props
Чому toValue треба викликати всередині watchEffect/computed: саме там Vue відстежує читання. Якщо розгорнути значення один раз на початку composable, зміни props.id вже не підхопляться.
Типова помилка з props:
useFetch(`/api/users/${props.id}`); // рядок обчислено один раз - не реактивно
useFetch(() => `/api/users/${props.id}`); // правильно
unref - старший аналог toValue, що не викликає геттери. У новому коді краще toValue.