Ссылки на элементы шаблона
Хотя декларативная модель рендеринга Vue абстрагирует от большинства прямых операций с DOM, все же могут быть случаи, когда нужен прямой доступ к базовым элементам DOM. Для этого можно использовать специальный атрибут ref
:
template
<input ref="input">
ref
- специальный атрибут, аналогичный атрибуту key
, о котором говорилось в главе v-for
. Он позволяет получить прямую ссылку на определенный элемент DOM или экземпляр дочернего компонента после его монтирования. Это может быть полезно, когда нужно, например, программно выставить фокус на поле ввода при монтировании компонента или инициализировать стороннюю библиотеку на элементе.
Доступ к ссылкам
Чтобы получить ссылку с помощью Composition API, мы можем использовать useTemplateRef()
хэлпер.
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
// the first argument must match the ref value in the template
const input = useTemplateRef('my-input')
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="my-input" />
</template>
При использовании Typescript, поддержка Vue IDE и vue-tsc
автоматически определят тип input.value
, основываясь для какого элемента или компонента используется соответствующий ref
атрибут.
Использование до версии 3.5
В версиях до 3.5, где не был введен useTemplateRef()
, нужно было объявлять ref
c именем, соответствующим значению атрибута ref
vue
<script setup>
import { ref, onMounted } from 'vue'
// объявляем ref-ссылку на элемент
// имя должно совпадать со значением ref в шаблоне
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
Если не используется <script setup>
, не забудьте также вернуть ссылку из setup()
:
js
export default {
setup() {
const input = ref(null)
// ...
return {
input
}
}
}
Обратите внимание, что вы можете получить доступ к ссылке только после того, как компонент был смонтирован. Если вы попытаетесь получить доступ к input
в шаблоне, при первом рендеринге она будет равна null
. Это происходит потому, что элемент не существует до завершения первого рендеринга!
Если попытаться следить за изменениями ссылки на элемент шаблона, обязательно учитывать случай, когда ссылка имеет значение null
:
js
watchEffect(() => {
if (input.value) {
input.value.focus()
} else {
// еще не смонтирован, или элемент был демонтирован (например: v-if)
}
})
См. также: Типизированные ссылки на элементы шаблона
Использование внутри v-for
Требуется v3.5 или выше
Когда ref
используется внутри v-for
, соответствующая ссылка должна содержать массив, который будет заполнен элементами после монтирования:
vue
<script setup>
import { ref, useTemplateRef, onMounted } from 'vue'
const list = ref([
/* ... */
])
const itemRefs = useTemplateRef('items')
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="items">
{{ item }}
</li>
</ul>
</template>
Использование до версии 3.5
В версиях до 3.5, где не был введен useTemplateRef()
, нужно было объявлять ref
c именем, соответствующим значению атрибута ref
. Ссылка также должна содержать значение массива:
vue
<script setup>
import { ref, onMounted } from 'vue'
const list = ref([
/* ... */
])
const itemRefs = ref([])
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="itemRefs">
{{ item }}
</li>
</ul>
</template>
Следует отметить, что массив ссылок не гарантирует тот же порядок, что и исходный массив.
Ссылка на функцию
Вместо строкового ключа атрибут ref
может быть привязан к функции, которая будет вызываться при каждом обновлении компонента и дает полную свободу выбора места хранения ссылки на элемент. Функция получает ссылку на элемент в качестве первого аргумента:
template
<input :ref="(el) => { /* присвоить el свойству или ссылке */ }">
Обратите внимание, что используется динамическое связывание :ref
, поэтому можно передать ему функцию вместо строки с названием ссылки. Когда элемент будет размонтирован, аргументом будет null
. Конечно, можно использовать метод вместо встроенной функции.
Ссылка на компонент
Этот раздел предполагает знание Компонентов. Не стесняйтесь пропустить его и вернуться позже.
ref
можно также использовать для дочернего компонента. В этом случае ссылка будет принадлежать экземпляру компонента:
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = useTemplateRef('child')
onMounted(() => {
// childRef.value will hold an instance of <Child />
})
</script>
<template>
<Child ref="child" />
</template>
Использование до версии 3.5
vue
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value будет содержать экземпляр <Child />
})
</script>
<template>
<Child ref="child" />
</template>
Если дочерний компонент использует Options API или не использует <script setup>
, то экземпляр ссылки будет идентичен экземпляру дочернего компонента this
, что означает, что родительский компонент будет иметь полный доступ к каждому свойству и методу дочернего компонента. Это позволяет легко создавать тесно связанные детали реализации между родительским и дочерним компонентами, поэтому ссылки на компонент следует использовать только в случае крайней необходимости - в большинстве случаев необходимо попытаться реализовать взаимодействие родителя и ребенка, используя стандартные интерфейсы props и emit.
Исключением здесь является то, что компоненты, использующие <script setup>
, являются приватными по умолчанию: родительский компонент, ссылающийся на дочерний компонент, использующий <script setup>
, не сможет получить доступ ни к чему, пока дочерний компонент не решит раскрыть публичный интерфейс с помощью макроса defineExpose
:
vue
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// Макросы компилятора, такие как defineExpose, не нужно импортировать
defineExpose({
a,
b
})
</script>
Когда родитель получает экземпляр этого компонента через ссылки шаблона, полученный экземпляр будет иметь вид { a: number, b: number }
(ссылки автоматически разворачиваются, как и для обычных экземпляров).
См. также: Типизированные ссылки на шаблоны компонентов