Skip to content

Ссылки на элементы шаблона

Хотя декларативная модель рендеринга Vue абстрагирует от большинства прямых операций с DOM, все же могут быть случаи, когда нужен прямой доступ к базовым элементам DOM. Для этого можно использовать специальный атрибут ref:

template
<input ref="input">

ref - специальный атрибут, аналогичный атрибуту key, о котором говорилось в главе v-for. Он позволяет получить прямую ссылку на определенный элемент DOM или экземпляр дочернего компонента после его монтирования. Это может быть полезно, когда нужно, например, программно выставить фокус на поле ввода при монтировании компонента или инициализировать стороннюю библиотеку на элементе.

Доступ к ссылкам

To obtain the reference with Composition API, we can use the useTemplateRef() helper:

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>

When using TypeScript, Vue's IDE support and vue-tsc will automatically infer the type of input.value based on what element or component the matching ref attribute is used on.

Usage before 3.5

In versions before 3.5 where useTemplateRef() was not introduced, we need to declare a ref with a name that matches the template ref attribute's value:

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
    }
  }
}

Полученная ссылка доступна через this.$refs:

vue
<script>
export default {
  mounted() {
    this.$refs.input.focus()
  }
}
</script>

<template>
  <input ref="input" />
</template>

Обратите внимание, что вы можете получить доступ к ссылке только после того, как компонент был смонтирован. Если вы попытаетесь получить доступ к $refs.inputinput в шаблоне, при первом рендеринге она будет равна undefinednull. Это происходит потому, что элемент не существует до завершения первого рендеринга!

Если попытаться следить за изменениями ссылки на элемент шаблона, обязательно учитывать случай, когда ссылка имеет значение 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>

Try it in the Playground

Usage before 3.5

In versions before 3.5 where useTemplateRef() was not introduced, we need to declare a ref with a name that matches the template ref attribute's value. The ref should also contain an array value:

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 используется внутри v-for, итоговым значением ссылки будет массив, содержащий соответствующие элементы:

vue
<script>
export default {
  data() {
    return {
      list: [
        /* ... */
      ]
    }
  },
  mounted() {
    console.log(this.$refs.items)
  }
}
</script>

<template>
  <ul>
    <li v-for="item in list" ref="items">
      {{ 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>
Usage before 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>
vue
<script>
import Child from './Child.vue'

export default {
  components: {
    Child
  },
  mounted() {
    // this.$refs.child будет содержать экземпляр <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 } (ссылки автоматически разворачиваются, как и для обычных экземпляров).

См. также: Типизированные ссылки на шаблоны компонентов

Опция expose может быть использована для ограничения доступа к дочернему экземпляру:

js
export default {
  expose: ['publicData', 'publicMethod'],
  data() {
    return {
      publicData: 'foo',
      privateData: 'bar'
    }
  },
  methods: {
    publicMethod() {
      /* ... */
    },
    privateMethod() {
      /* ... */
    }
  }
}

В приведенном выше примере родитель, ссылающийся на этот компонент через ссылку, сможет получить доступ только к publicData и publicMethod.

Ссылки на элементы шаблонаУже загружено