Vue 3 introduces distinct concepts of ref for reactivity and template refs for accessing DOM elements or component instances.
ref: Reactive References
ref creates reactive data references in Composition API.
Basic Usage
import { ref } from 'vue'
const counter = ref(0)
console.log(counter.value) // Output: 0
counter.value++
console.log(counter.value) // Output: 1
Key Characteristics
- Wraps primitive values: Converts basic types (numbers, strings, booelans) into reactive objects
- Requires .value access: Must use
.valueto read or modify in JavaScript - Automatic unwrapping: Templates automatically unwrap refs without
.value
<template>
<div>{{ counter }}</div> <!-- No .value needed -->
</template>
Handling Complex Data
For objects and arrays, ref internally uses reactive:
const profile = ref({
name: 'John Doe',
age: 30
})
profile.value.age = 31
Template Refs: Accessing DOM and Components
Template refs provide direct access to DOM elements or component instances.
Vue 2 Implementation
In Vue 2, references were accesssed via this.$refs:
<template>
<div>
<input ref="inputField">
<ChildComponent ref="childComp"/>
</div>
</template>
<script>
export default {
mounted() {
this.$refs.inputField.focus()
this.$refs.childComp.executeMethod()
}
}
</script>
Vue 3 Composition API Aproach
Template refs usage changed in Vue 3:
- Declare ref variables
- Bind to elements/components
- Access via .value after mounting
<template>
<div>
<input ref="inputElement">
<ChildComponent ref="childInstance"/>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const inputElement = ref(null)
const childInstance = ref(null)
onMounted(() => {
inputElement.value.focus()
childInstance.value.executeMethod()
})
</script>
Dynamic References in v-for
Use function-based refs in loops:
<template>
<ul>
<li
v-for="(fruit, idx) in fruitList"
:key="idx"
:ref="element => { if (element) fruitRefs[idx] = element }"
>
{{ fruit }}
</li>
</ul>
</template>
<script setup>
import { ref, onBeforeUpdate } from 'vue'
const fruitList = ref(['Apple', 'Banana', 'Orange'])
const fruitRefs = ref([])
onBeforeUpdate(() => {
fruitRefs.value = []
})
</script>
Migration Example: Vue 2 to Vue 3 with TypeScript
Consider migrating a complex Vue 2 component using dynamic refs in v-for:
Original Vue 2 template:
<template>
<MixerControl
v-for="(item, index) in inputGroups"
:key="index"
:ref="`input-${index}`"
/>
</template>
Vue 2 ref access pattern:
const refMap = {
'input': `input-${channel}`
}
const refName = refMap[type]
const componentRefs = this.$refs[refName]
Vue 3 Migration Steps
- Declare reference arrays
const inputControlRefs = ref([])
- Bind in template
<MixerControl
v-for="(item, idx) in inputGroups"
:key="idx"
:ref="element => { if (element) inputControlRefs.value[idx] = element }"
/>
- Access components
inputControlRefs.value[0].invokeMethod()
Key Differences and Migration Summary
- ref: Creates reactive data, requires
.valuein JavaScript - Template refs: Access DOM/components
- Vue 2:
this.$refscollection - Vue 3: Declared ref variables bound in template
- Vue 2:
- Migration approach:
- Replace string refs with function-based bindings
- Use ref arrays for v-for elements
- Access via
.valueafter component mount
This approach aligns with Vue 3's Composition API and improves TypeScript support.