Vue сам керує DOM, але інколи потрібен прямий доступ до елемента: фокус, прокрутка, вимір розміру, ініціалізація сторонньої бібліотеки. Для цього - template ref.
Vue 3.5+ - useTemplateRef:
<script setup>
import { useTemplateRef, onMounted } from 'vue';
const input = useTemplateRef('search');
onMounted(() => input.value.focus());
</script>
<template>
<input ref="search" type="search">
</template>
Рядок у useTemplateRef('search') збігається зі значенням атрибута ref.
До 3.5 - ref з тією самою назвою, що й атрибут:
const search = ref(null); // <input ref="search">
Цей спосіб працює й далі, але useTemplateRef явніший: видно, що змінна - саме посилання на елемент, і не потрібно збігу назв змінних.
Коли ref заповнений:
- після монтування - в
setupіonBeforeMountвінnull; - під
v-ifстаєnull, коли елемент прибрано, - звідси перевіркиinput.value?.focus(); - після зміни стану DOM оновлюється асинхронно: щоб звернутися до щойно показаного елемента, потрібен
await nextTick():
async function openEditor() {
editing.value = true;
await nextTick();
editorInput.value.focus();
}
Ref у v-for дає масив елементів (порядок не гарантовано збігається з масивом даних):
const items = useTemplateRef('items'); // <li v-for="..." ref="items">
Ref-функція - для повного контролю: :ref="(el) => { if (el) map.set(id, el) }" викликається з елементом при монтуванні й з null при видаленні.
Ref на компонент дає екземпляр компонента - але з <script setup> доступне лише те, що відкрито через defineExpose.
Коли НЕ треба ref: змінювати вміст, класи, атрибути напряму через DOM (el.textContent = ..., el.classList.add). Наступний рендер Vue перезапише зміни, а стан стане неузгодженим. Усе, що можна виразити через стан і шаблон, - через стан і шаблон.