До Vue 3.5 деструктуризація props губила реактивність: const { title } = defineProps(...) давала звичайне значення на момент створення компонента.
З Vue 3.5 деструктуризація з defineProps реактивна:
<script setup>
const { title, count = 0 } = defineProps(['title', 'count']);
</script>
<template>
<h1>{{ title }}</h1>
<p>{{ count }}</p>
</template>
Компілятор <script setup> переписує звернення title на __props.title, тож значення завжди актуальне. Заодно це найзручніший спосіб задати значення за замовчуванням - звичайним синтаксисом JavaScript замість withDefaults.
Пастка - передача у функцію чи watch:
const { id } = defineProps(['id']);
watch(id, () => { /* ... */ }); // помилка компіляції: передано значення, а не джерело
watch(() => id, () => { /* ... */ }); // правильно: геттер
useFetch(id); // composable отримає число, а не реактивне джерело
useFetch(() => id); // правильно
Деструктуризована змінна виглядає як звичайна змінна, але реактивна лише там, де компілятор може переписати звернення. Передаючи її кудись, треба загорнути в геттер, - інакше функція отримає «знімок» значення.
Спостереження за props без деструктуризації:
const props = defineProps(['id', 'filters']);
watch(() => props.id, (id) => load(id));
watch(() => props.filters, reload, { deep: true }); // об'єкт - глибоко
watch(props.id, ...) - та сама помилка: значення замість джерела.
Що варто знати:
- props змінювати не можна - вони належать батьківському компоненту. Для локальної копії -
ref(props.initial)(свідомо не реактивна до змін батька) абоcomputed; - об'єкти в props передаються за посиланням, і зміна їх полів технічно спрацює, але порушує односпрямований потік даних - батько не знатиме, хто змінив його стан;
- для
v-modelна компоненті -defineModel(), а не зміна props.