Vue
Template refs и useTemplateRef
Форма логина должна автоматически фокусировать поле email при открытии модального окна. Декларативно это сделать нельзя: фокус - это императивный вызов element.focus(), которого нет в шаблонной модели. Нужен прямой доступ к настоящему DOM-узлу input. Vue даёт его через template ref: атрибут ref на элементе, и после монтирования в переменной появляется сам элемент, на котором можно вызвать focus(), measure размеров или подключить стороннюю библиотеку.
- Автофокус поля при открытии модалки или перехода на шаг формы
- Воспроизведение видео или аудио: доступ к нативному элементу video для play() и pause()
- Интеграция императивных библиотек: передача canvas в Chart.js или контейнера в Mapbox
- Измерение размеров элемента через getBoundingClientRect для позиционирования тултипа
- Вызов публичного метода дочернего компонента: открыть диалог, сбросить валидацию формы
Предварительные знания
- Синтаксис script setup и объявление ref внутри него
- Понимание жизненного цикла: onMounted и почему до монтирования DOM ещё нет
- Базовое знание директивы v-for для списков
ref на элементе и useTemplateRef
Чтобы получить настоящий DOM-узел, на элементе ставится атрибут ref. Классический способ: объявить в скрипте ref с тем же именем, что в атрибуте, и Vue после монтирования положит туда сам элемент. До монтирования значение равно null, поэтому обращаться к элементу нужно в onMounted.
Vue 3.5 ввёл useTemplateRef. В шаблоне остаётся атрибут ref со строкой, а в скрипте элемент достаётся по этой строке, без необходимости держать имя переменной синхронным с атрибутом. Это удобнее при рефакторинге и точнее типизируется в TypeScript.
Template ref - это не способ управлять DOM вместо реактивности. Большая часть интерфейса описывается декларативно через состояние. Template ref нужен только для императивных действий, которых нет в шаблонной модели: focus, play, measure, интеграция библиотек.
Почему обращение к template ref в onMounted, а не сразу в setup?
Доступ к дочернему компоненту и defineExpose
Template ref работает и на компоненте. Тогда .value содержит экземпляр дочернего компонента. Но есть важное отличие: компонент на script setup закрыт по умолчанию. Снаружи не видно ни его переменных, ни методов. Чтобы родитель мог вызвать метод ребёнка, ребёнок должен явно опубликовать его через defineExpose.
Закрытость по умолчанию - это инкапсуляция. Родитель не должен лазить во внутренности ребёнка напрямую. defineExpose превращает выбранные методы в осознанный публичный контракт компонента, как public-методы класса.
Родитель получил ref на дочерний компонент, но его метод reset() не виден. В чём причина?
Refs внутри v-for
Когда ref стоит на элементе внутри v-for, нужен не один элемент, а коллекция. В Vue 3.5 useTemplateRef на элементе внутри v-for собирает массив всех отрисованных элементов. Это удобно для измерения каждого пункта списка, скролла к конкретному элементу или подключения наблюдателя к каждому.
Порядок элементов в массиве соответствует порядку отрисовки, но Vue не гарантирует его при динамических изменениях списка. Если важна привязка элемента к конкретному пункту данных, надёжнее хранить элементы в Map по ключу, заполняя его через функцию-ref.
Функция-ref (:ref="el => ...") вызывается при монтировании и размонтировании каждого элемента и даёт полный контроль над сбором. Это запасной путь, когда массива от useTemplateRef недостаточно, например при необходимости соответствия элемент-ключ.
Как useTemplateRef ведёт себя для ref на элементе внутри v-for в Vue 3.5?
Связь с другими темами
Урок опирается на script setup и вводит паттерн composable-доступа:
- script setup — useTemplateRef и defineExpose - макросы, доступные только внутри script setup
- ref и reactive — Template ref устроен как обычный ref, чьё значение заполняет сам Vue
- Vue Router — useRoute и useRouter - тот же приём composable-доступа к ресурсу, что и useTemplateRef
Итог
- Template ref даёт прямой доступ к реальному DOM-элементу или экземпляру дочернего компонента для императивных операций
- Классический способ: ref('') в скрипте плюс атрибут ref с тем же именем в шаблоне; в Vue 3.5 появился useTemplateRef('name') без привязки имён
- Элемент доступен только после монтирования, поэтому обращаться к нему нужно в onMounted или позже, а не сразу в setup
- По умолчанию у дочернего компонента наружу видно ничего; defineExpose явно публикует методы и свойства для родителя
- Внутри v-for ref на элементе с useTemplateRef собирает массив элементов (порядок не гарантирован)
Связанные уроки
- vue-09-script-setup — useTemplateRef и defineExpose живут внутри script setup, поэтому нужно понимать этот синтаксис
- vue-06-ref-reactive — Template ref - это обычный ref, чьё .value Vue заполняет ссылкой на элемент, так что база про ref необходима
- vue-24-router-intro — useRoute и useRouter - такие же composable-функции для доступа к внешнему ресурсу, как useTemplateRef к элементу