Changes in component template refs
Changes in Component Template References (ref)
In Vue.js, ref
is a special attribute used to directly access DOM elements or child component instances in templates. With the introduction of Vue 3, the usage of ref
has undergone some significant changes, particularly in how it behaves in the Composition API compared to the Options API.
Basic Usage of ref
In Vue 2, ref
was primarily used to obtain DOM elements or component instances:
<template>
<div ref="myDiv">This is a div element</div>
<child-component ref="child"></child-component>
</template>
<script>
export default {
mounted() {
console.log(this.$refs.myDiv) // Get DOM element
console.log(this.$refs.child) // Get child component instance
}
}
</script>
In Vue 3, this usage remains valid, but in the Composition API, it's recommended to use the new ref
function:
<template>
<div ref="myDiv">This is a div element</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const myDiv = ref(null)
onMounted(() => {
console.log(myDiv.value) // Get DOM element
})
</script>
Changes to ref in the Composition API
Vue 3's Composition API introduced a more flexible ref
system. In the <script setup>
syntax, template references become more intuitive:
<template>
<input ref="inputRef" />
<button @click="focusInput">Focus Input</button>
</template>
<script setup>
import { ref } from 'vue'
const inputRef = ref(null)
function focusInput() {
inputRef.value.focus()
}
</script>
When using ref
to reference a component, you can access the component's public properties:
<template>
<child-component ref="childRef" />
</template>
<script setup>
import { ref } from 'vue'
const childRef = ref(null)
// Access the child component's public method
childRef.value.someMethod()
</script>
Combining ref with v-for
When ref
is used inside v-for
in Vue 2, it returns an array:
<template>
<div v-for="item in items" :key="item.id" ref="itemRefs">
{{ item.text }}
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' }
]
}
},
mounted() {
console.log(this.$refs.itemRefs) // Array form
}
}
</script>
In Vue 3, this behavior has changed. To get an array of references, you need to use a function-style ref
:
<template>
<div v-for="item in items" :key="item.id" :ref="setItemRef">
{{ item.text }}
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const items = ref([
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' }
])
const itemRefs = ref([])
function setItemRef(el) {
if (el) {
itemRefs.value.push(el)
}
}
onMounted(() => {
console.log(itemRefs.value) // Array containing all DOM elements
})
</script>
Dynamic ref Names
In some cases, you may need to dynamically specify the name of a ref
:
<template>
<div :ref="dynamicRef">Dynamic ref</div>
</template>
<script setup>
import { ref } from 'vue'
const dynamicRef = ref('myDynamicRef')
// Or use a function form
function setDynamicRef(el) {
// Handle the reference
}
</script>
Type Inference for ref
In TypeScript projects, you can add type annotations to ref
for better type support:
<script setup lang="ts">
import { ref } from 'vue'
import type { MyComponent } from './MyComponent.vue'
const myComponentRef = ref<InstanceType<typeof MyComponent>>()
const inputRef = ref<HTMLInputElement | null>(null)
</script>
ref with Reactive Objects
When you need to organize multiple references together, you can use a reactive object:
<template>
<form ref="formRef">
<input ref="inputRefs.username" />
<input ref="inputRefs.password" />
</form>
</template>
<script setup>
import { reactive } from 'vue'
const formRef = ref(null)
const inputRefs = reactive({
username: null,
password: null
})
</script>
Controlling Exposed refs in Components
In Vue 3, components can use defineExpose
to explicitly specify which content can be accessed via ref
:
<!-- ChildComponent.vue -->
<script setup>
import { ref } from 'vue'
const internalState = ref('secret')
const publicMethod = () => {
console.log('This is a public method')
}
defineExpose({
publicMethod
})
</script>
<!-- ParentComponent.vue -->
<template>
<child-component ref="childRef" />
</template>
<script setup>
import { ref } from 'vue'
const childRef = ref(null)
// Only publicMethod is accessible, internalState is not
childRef.value.publicMethod()
</script>
Performance Considerations for ref
While ref
is convenient, overusing it may impact performance, especially in large lists. Vue's reactivity system needs to track these references, so you should:
- Avoid creating numerous
ref
s when unnecessary - Clean up unnecessary references when components unmount
- For static elements, consider using native methods like
document.getElementById
<script setup>
import { onUnmounted } from 'vue'
const elementRef = ref(null)
onUnmounted(() => {
elementRef.value = null // Aids garbage collection
})
</script>
ref and nextTick
Since DOM updates are asynchronous, sometimes you need to wait for nextTick
to access the latest ref
:
<template>
<div v-if="show" ref="conditionalRef">Conditionally rendered content</div>
<button @click="toggle">Toggle</button>
</template>
<script setup>
import { ref, nextTick } from 'vue'
const show = ref(false)
const conditionalRef = ref(null)
async function toggle() {
show.value = !show.value
await nextTick()
console.log(conditionalRef.value) // Now safe to access
}
</script>
Function-style ref
In addition to string and ref
variables, you can also use a function form to set references:
<template>
<div :ref="el => { if (el) divElement = el }">Function-style ref</div>
</template>
<script setup>
import { ref } from 'vue'
let divElement = ref(null)
</script>
This form is particularly useful for dynamic components or when you need finer control over references.
ref with Teleport
When using <Teleport>
, the behavior of ref
also requires attention:
<template>
<Teleport to="body">
<div ref="teleportedRef">Teleported content</div>
</Teleport>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const teleportedRef = ref(null)
onMounted(() => {
console.log(teleportedRef.value) // Still correctly referenced
})
</script>
Using ref in Render Functions
In render functions, ref
needs to be passed via the second parameter:
import { h, ref } from 'vue'
const myRef = ref(null)
export default {
setup() {
return () => h('div', { ref: myRef }, 'Content')
}
}
Or in JSX:
const myComponent = () => {
const inputRef = ref(null)
return <input ref={inputRef} />
}
ref with Custom Directives
When both ref
and a custom directive are applied to an element, the execution order matters:
<template>
<div v-my-directive ref="myRef"></div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const myRef = ref(null)
const vMyDirective = {
mounted(el) {
console.log('Directive mounted')
}
}
onMounted(() => {
console.log('ref available:', myRef.value)
})
// Output order: Directive mounted -> ref available
</script>
ref with KeepAlive
When a component is cached by <KeepAlive>
, ref
behavior remains consistent:
<template>
<KeepAlive>
<child-component v-if="show" ref="childRef" />
</KeepAlive>
<button @click="show = !show">Toggle</button>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(true)
const childRef = ref(null)
// ref remains stable during toggling
</script>
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn