Олег Марков
Использование ref для управления ссылками и реактивностью в Vue 3
Введение
В Vue 3 одним из главных нововведений стал Composition API — новый способ организации реактивности и управления состоянием компонентов. Одним из важных элементов Composition API является функция ref
. Она выступает как универсальный инструмент для создания реактивных данных и получения ссылок на элементы DOM.
Использование ref позволяет создавать реактивные примитивы (например, числа, строки, логические значения) и удобно работать с ними в реактивном контексте. Помимо этого, ref открывает дополнительные возможности по ссылкам на HTML-элементы, что важно для манипуляций DOM в рамках шаблонов.
Давайте вместе разберёмся, как устроен ref, где и как его рационально применять, с какими особенностями его использования стоит знакомиться разработчикам Vue 3.
Что такое ref и зачем он нужен
Основы синтаксиса и концепция
В Vue 3 Composition API, чтобы сделать переменную реактивной, вы вызываете функцию ref(value)
, передавая в неё исходное значение. Возвращается реактивный объект, который можно безопасно использовать и изменять, не теряя реактивности.
Давайте посмотрим простой пример:
import { ref } from 'vue'
export default {
setup() {
// Создаём реактивное число с начальными данными 0
// count теперь объект типа Ref
const count = ref(0)
// В шаблоне мы сможем обращаться к count, чтобы отобразить его
return { count }
}
}
ref
удобно использовать для реактивных примитивов, например, для счетчиков, флагов, строк, которые не получится сделать реактивными с помощью reactive
(этот способ лучше подходит только для объектов и массивов).
Reactivity и значение .value
В отличие от обычных переменных, чтобы получить или задать значение для ref-переменной, нужно обращаться к свойству .value
. Единственное исключение — шаблон компонента, внутри которого Vue автоматически прокидывает это свойство.
Пример:
const count = ref(0)
console.log(count.value) // Получаем текущее значение
count.value++ // Изменяем его
В шаблоне делать это вручную не нужно:
<template>
<button @click="count++">Clicked {{ count }} times.</button>
</template>
Создание реактивных переменных с ref
Примитивные значения
Когда вы хотите создать реактивную переменную для числа, строки, булевого значения, используйте ref:
const message = ref('Привет, мир!')
const flag = ref(false)
const score = ref(10)
Теперь при изменении .value
этих переменных связанные части шаблона автоматически обновятся.
Массивы и объекты
Ref также поддерживает работу с объектами и массивами. Но есть нюансы! Когда вы кладёте в ref
объект или массив, сам контейнер остаётся ref, и обращаться к данным следует через .value
, а его внутренности Vue делает реактивными автоматически.
Смотрите, как это выглядит:
const user = ref({
name: 'Мария',
age: 23
})
// Изменяем реактивные свойства через .value
user.value.name = 'Игорь'
user.value.age++
Этот подход жизненно важен тогда, когда ваш объект может быть полностью заменён.
Особенность: ref и reactive
Если вы используете только объекты — есть альтернатива: reactive
сразу делает объект реактивным без обёртки .value
:
import { reactive } from 'vue'
const state = reactive({ count: 0, theme: 'light' })
state.count++ // Работает напрямую
Но если объект может быть заменён целиком или это примитив — выбирайте ref. Также ref в любом случае универсальнее: его всегда можно использовать как в JS-коде, так и в шаблоне, где "из коробки" разворачивается без необходимости указывать .value
.
Использование ref для доступа к DOM-элементам
ref играет особую роль в работе с DOM, ведь иногда в шаблоне нужно получить доступ к конкретному элементу: например, сфокусировать input, измерить его размеры, среагировать на событие, для которого не хватает обычных слушателей.
Давайте разберём пример.
Как получить ссылку на DOM-узел
- Создайте переменную с помощью ref без значения:
const inputRef = ref(null)
- Свяжите её с элементом через директиву
ref
в шаблоне:
<input ref="inputRef" />
- Теперь доступно свойство
.value
, содержащее DOM-элемент после монтирования компонента:
onMounted(() => {
if (inputRef.value) {
inputRef.value.focus() // Установим фокус сразу
}
})
Динамическое управление элементами
ref работает и для массивов или компонентов (например, при рендеринге v-for):
<div v-for="(item, idx) in list" :key="item.id" :ref="setItemRef"></div>
В JS-коде:
const itemRefs = ref([])
function setItemRef(el) {
if (el) itemRefs.value.push(el)
}
Вот здесь очень важно: хранить DOM-элементы или компоненты через ref, иначе Vue не сможет правильно отслеживать изменения, особенно при динамическом удалении или добавлении элементов.
Reactivity: особенности реактивности ref
Как ref работает «под капотом»
Vue 3 реализует реактивность через прокси-объекты, которые перехватывают чтение и запись значения. Когда вы используете ref
, он создаёт объёртку с геттером/сеттером для .value
. Это даёт возможность засечь изменение — и запустить обновление компонента, если переменная участвует в вычислениях или шаблоне.
Автоматическое «разворачивание» в шаблонах
В шаблоне компонента обращаться к ref можно без .value
:
<span>{{ count }}</span> <!-- автоматически count.value -->
Всё происходит автоматически, Vue это делает ради удобства и совместимости.
Реактивность и объекты-массивы внутри ref
К примеру:
const arr = ref([1, 2, 3])
arr.value.push(4) // Это изменение тоже вызовет обновление!
Это удобно — Vue отслеживает изменения как структуры .value
, так и её содержимого.
Использование с вычисляемыми и наблюдаемыми значениями
Работу с ref можно комбинировать с computed и watch:
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
watch(count, (newVal, oldVal) => {
console.log('count изменился:', oldVal, '→', newVal)
})
В этом примере вы видите, что ref прекрасно встраивается в полноценную реактивную связку приложения: его можно наблюдать, вычислять на основе него производные значения и всё это всегда будет согласовано.
Возможности ref в Composition API
Передача ref между компонентами
Вы можете передавать ref в дочерние компоненты:
// Родитель
<ChildInput :inputRef="inputRef" />
// Дочерний компонент
props: {
inputRef: Object
},
mounted() {
this.inputRef.value.focus()
}
Это очень удобно, если нужно сфокусировать элемент или получить ссылку на него из родителя.
Совместное использование с шаблонной ссылкой
Vue позволяет вызывать несколько ссылок через ref, комбинируя Composition API и шаблонные рефы. Заметим, что доступ к DOM появится только после хуков mounted или updated.
Управление коллекциями DOM-узлов
Если работаете с v-for и директивой ref, сохранять коллекцию объектов ещё важнее. Для этого чаще всего используют массив или объект:
const items = ref([])
function setItemRef(el, idx) {
if (el) items.value[idx] = el
}
Шаблон:
<div v-for="(item, idx) in list" :ref="el => setItemRef(el, idx)">
{{ item.name }}
</div>
Теперь вы свободно распоряжаетесь ссылками на все элементы списка.
Советы и "подводные камни" при работе с ref
1. Никогда не используйте ref без .value в коде JS
За пределами шаблона всегда обращайтесь к свойству .value
! Так вы гарантируете корректную реактивность данных.
2. Именование переменных
Для ref переменных часто используют суффикс Ref или префикс is для булевых значений, например: countRef, inputRef, isLoading.
3. Не изменяйте напрямую свойство value в шаблоне с помощью выражений
Лучше всё управление оставить для JS-кода или событий, чтобы избежать непредсказуемого поведения.
4. Разница между ref и reactive для объектов
ref
идеально подходит для реактивных примитивов и случаев, когда объект должен заменяться полностью (myObj.value = {...}
). reactive
хорошо работает, если нужно модифицировать значения объекта напрямую.
5. Не используйте ref только ради передачи данных
Если переменная не изменяется и не привязывается к реактивности/DOM, используйте обычную переменную/props.
Заключение
Функция ref
— это не только ключевой механизм работы Composition API в Vue 3, но и практичный инструмент для гибкой работы с состоянием компонентов и ссылками на DOM. Благодаря ref можно просто объявлять реактивные данные, безопасно модифицировать их и отслеживать изменения, комбинировать с другими реактивными инструментами, а также легко управлять доступом к компонентам и элементам разметки.
Когда вы поймёте, как именно рефы устроены "под капотом" и освоите практику их использования, ваши компоненты станут проще, а приложение — надёжнее и гибче.
Частозадаваемые технические вопросы по теме статьи и ответы на них
Какие типы значений нельзя делать реактивными с помощью ref?
С помощью ref нельзя сделать реактивными несерийные типы вроде функций или Symbol. Кроме того, ref не сделает реактивными изменения, происходящие внутри встроенных объектов вроде Map, Set или Date. Если нужно хранить функцию в состоянии, используйте обычную переменную.
Как «обернуть» сторонний объект так, чтобы его свойства стали реактивными через ref?
Если нужно, чтобы свойства были реактивными, лучше использовать reactive. Например:
js
const apiData = reactive(externalObject)
Однако если требуются только замены или отслеживание ссылок на весь объект, ref подойдёт больше.
Как очистить ссылку ref на DOM-элемент при его удалении?
Vue сам очищает свойства ref, когда элемент удаляется. В JS-коде достаточно проверять ref.value на null:
js
if (elRef.value) {
// Элемент существует
}
После удаления elRef.value станет null.
Можно ли прокидывать ref через provide/inject?
Да, Vue допускает передачу рефов через provide/inject, что удобно для глобальных ссылок или состояний:
js
provide('myRef', myRef)
const myRef = inject('myRef')
Как отслеживать изменения внутри массива/объекта, сохранённого в ref?
Vue автоматически отслеживает добавление, удаление, изменение свойств массивов и объектов внутри ref:
js
const arr = ref([])
watch(arr, () => { /* реагирует на любые изменения */ })
Для глубокой слежки используйте опцию { deep: true }, если требуется:
js
watch(arr, handler, { deep: true })